cswork / libraryOpen practice workspace

Interview guides · BMO Financial Group

BMO Financial Group Software Engineer Interview Guide

BMO Financial Group Software Engineer interview guide: reported rounds, debugging, loan-service design, mainframe and behavioral questions, 7-day plan.

The 60-second brief

BMO Financial Group is a bank, and candidates describe the Software Engineer role as building and maintaining applications for its banking operations. The reported range runs from loan servicing platforms to cloud-based AI enablement, and some teams work on modernizing mainframe systems or on cloud-native AI deployments. Reported topic tags for the loop are Cloud Engineering, Artificial Intelligence (AI) Enablement, Mainframe Application Development, Loan Servicing Systems and Software Application Development. Which of those you will be judged on depends on the team and the level of the opening.

This guide is for candidates applying to a Software Engineer opening at BMO Financial Group, from an intermediate developer up to a principal engineer, since candidates report openings across that span. It covers the two reported stages, works through the ten questions candidates report (debugging a distributed system, mainframe maintainability, AI/ML in production, data consistency, a secure loan-processing service, monolith versus microservices, and four behavioral prompts), and ends with a 7-day plan. It does not cover pay.

Candidates report two stages over roughly 2-4 weeks: an Initial Screening, then Technical Deep-Dives that are specific to your area, whether that is cloud, mainframe or application development.

Focus areas

  • Cloud Engineering
  • Artificial Intelligence (AI) Enablement
  • Mainframe Application Development

Overview

The first decision in your preparation is which track you are on. Candidates describe openings that range from Mainframe Application Development to Principal Cloud Engineering, with loan servicing and AI enablement in between. Read your job description and sort every technology in it into one of those tracks. A mainframe-heavy candidate should spend more of the week on modernization and interoperability. A cloud candidate should spend it on containers, orchestration, serverless, identity and encryption, and on the AI/ML lifecycle from training through monitoring.

The reported questions are scenario and experience questions, not algorithm puzzles. Four are technical: debugging a performance problem in a distributed system, keeping legacy mainframe code maintainable and scalable, integrating AI/ML models into production workflows, and managing data consistency in a cloud-native environment. Two are design questions: architecting a secure, high-availability loan-processing service, and choosing between a monolith and microservices for a new project. The remaining four are stories about pivoting under constraints, explaining a trade-off to a non-technical stakeholder, handling a technical disagreement, and taking over a stalled project.

Reports stress explaining why you made an architectural choice, not only how you built it, and they stress security and regulatory compliance for a financial institution. In practice that means every design answer should name its failure modes, its audit and access-control story, and the consistency it needs. Every story should name your decision, the alternative you rejected and a measurable result. Prepare one or two projects you can describe at the level of components, trade-offs and numbers, and reuse them across the technical and behavioral questions.

The BMO Financial Group interview process

Candidates report two stages: an Initial Screening followed by role-specific Technical Deep-Dives. Reports do not say which of the ten questions belong to which stage, so prepare all of them for the deep-dives.

Initial Screening

Candidates describe the Initial Screening as a first conversation to check fit for the role. Reports give no format or question list for it, so treat it as the place where you state your track clearly. Say which of mainframe, cloud, AI enablement, loan servicing or general application development matches your recent work, and back each claim with one concrete project. Have the behavioral stories ready as well, since the reported behavioral questions are about collaboration with product, security and operations people.

What to demonstrate

  • Fit between your background and the opening, as the stage is described
  • How clearly you summarize your experience and your technical area
  • Whether your recent stack matches the one in the job description

How to prepare

  • Write a two-minute walkthrough of your career that ends on the stack named in your job description, and say it aloud until it is steady.
  • Pick two projects and note for each the scale, your own decisions, one trade-off and one measurable result.
  • Prepare a short, honest reason for choosing banking and for choosing this team's track, grounded in the work you have done.

Technical Deep-Dives

Candidates describe the Technical Deep-Dives as role-specific assessments focused on your area of expertise. The reported questions fall into three groups: technical (distributed-system debugging, mainframe maintainability, AI/ML in production, data consistency), design (a secure high-availability loan-processing service, monolith versus microservices) and behavioral. Answer each from a structure: requirements and constraints, the design or the diagnosis, the trade-offs, the failure modes, and how you would verify the result. Tie the answer to your own past work wherever you can.

What to demonstrate

  • Depth in your core language, platform and tools, as reported for this stage
  • Ability to design a secure, available and compliant service from vague requirements
  • Reasoning about legacy systems, data consistency and production AI/ML
  • Whether you can justify an architectural choice with its trade-offs

How to prepare

  • Rehearse the loan-processing design aloud: entry points, authentication and authorization, encryption in transit and at rest, idempotent writes, audit trail, redundancy across zones, and what happens when a dependency fails.
  • Build a debugging checklist for the distributed performance question: define the symptom with metrics, find the slow hop with traces, check saturation and queues, then confirm the fix with a before and after measurement.
  • Write a half-page comparison of monolith and microservices that ends in a decision rule tied to team size, deployment independence, data ownership and operational cost.
  • Prepare a mainframe or legacy modernization example, such as wrapping a batch job behind an API, even if you must borrow it from a similar legacy system you have worked on.

Where candidates lose points

Describing a loan-processing design as a list of boxes with no security or failure story

Open with requirements, then cover authentication and authorization, encryption, an audit trail, idempotent request handling so a retry cannot post a payment twice, and redundancy across zones. Finish by saying what happens when each dependency fails.

Choosing microservices (or a monolith) by default in the architecture-choice question

State a decision rule based on team size, independent deployment needs, data ownership and operating cost. Say when you would start with a modular monolith and what signal would make you split it later.

Jumping to a fix in the distributed-systems debugging question before measuring anything

Follow a fixed sequence: define the symptom with a metric, locate the slow hop with traces, check saturation, queueing and recent changes, form one hypothesis at a time, and confirm the fix with a before and after comparison.

Treating the legacy and mainframe question as a rewrite pitch

Talk about incremental change: tests around existing behavior first, an API layer over stable jobs, small refactors behind that layer, and a rollback plan for each step. Tie it to keeping business processes running.

Telling behavioral stories in the plural, with no personal decision, alternative or result

Use I for your own actions, name the option you rejected, and end each story with a number or an observable outcome. Prepare one story each for a pivot, a stakeholder explanation, a disagreement, a stalled project and an AI/ML integration.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Track a rolling failure rate per destination for circuit decisions

Easy · sliding window · ring buffer · circuit breaker

The egress service delivers about 1,500 webhooks per second across roughly 40,000 destinations, each call bounded by a 10 second timeout. Maintain, per destination, the failure rate over the trailing 60 seconds so a caller can ask before dispatch whether the circuit should open. Attempts arrive as (destination_id, finished_at_ms, outcome). Requirement: amortised O(1) per attempt, with total memory bounded by the destination count rather than by traffic. Give the structure, its exact memory, and the rule that stops a destination with three attempts from opening a circuit.

Approach

  1. Name the exact-deque version and then reject it as the default. Holding timestamps and advancing a tail pointer past anything older than now minus 60 seconds is a correct two-pointer window at amortised O(1) per attempt, but its memory tracks in-window traffic, so one destination in a retry storm holds hundreds of thousands of entries while thousands of quiet destinations hold none.
  2. Use a ring of 60 one-second buckets per destination, each bucket a pair of counters for attempts and failures. On an attempt, advance the ring by the elapsed whole seconds, zeroing at most min(elapsed, 60) buckets, then increment the head. That is amortised O(1) with a fixed footprint per destination.
  3. State the footprint: 60 buckets times two 4-byte counters is 480 bytes of payload per destination, so 40,000 destinations is roughly 20 to 25 MB with per-entry overhead, bounded by the catalogue rather than by the rate. The cost is granularity, since the oldest bucket ages out in whole seconds, which is far tighter than the decision needs.
  4. Require a minimum sample before the circuit may open. A destination with three attempts and three failures reads as 100 percent and is not evidence; a floor of roughly 20 attempts in the window makes the ratio meaningful, and below that floor use a run of consecutive failures as the trigger instead.

Follow-up

  • The fleet is 30 instances and each sees roughly a thirtieth of a destination's traffic. Where does the rate actually live, and what does a per-instance answer get wrong?
  • A destination answers in 9.5 seconds and succeeds. It is not failing but it is consuming your per-destination concurrency. What signal should open the circuit here?

Diff a projection against the primary without per-row point reads

Hard · reconciliation · range hashing · throttling

The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.

Approach

  1. Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
  2. Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
  3. For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
  4. Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.

Follow-up

  • The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
  • Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Stop tag and share joins from fanning out a page

Medium · join fan-out · semi-joins · many-to-many

resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.

Approach

  1. Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
  2. Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
  3. Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
  4. Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.

Follow-up

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?

How do you ensure code maintainability and scalability when working with legacy mainframe applications?

Medium · Technical and Domain Proficiency

Approach

  1. Write characterisation (golden-master) tests before changing anything: run known inputs through the existing batch jobs or transactions, capture the outputs byte for byte, and treat any difference as a regression. Legacy code rarely has a spec, so the current behaviour is the spec.
  2. Modernise with the strangler-fig pattern: put an API facade in front of the existing transactions, move one capability at a time to new code behind it, and keep the mainframe as the system of record until each piece has passed a parallel run with reconciled outputs.
  3. Know where integration bugs come from: record layouts defined in copybooks, EBCDIC-to-ASCII conversion, packed-decimal (COMP-3) and fixed-point amounts that must never pass through binary floating point, and batch windows that downstream systems depend on.
  4. Scale reads without loading the core: feed a read model or cache from change data capture or batch extracts, so new channels query that copy and the mainframe keeps handling the writes.
  5. Refactor in small, reversible steps: extract duplicated logic into called subprograms, remove dead paragraphs only once production logs show they never run, and ship each change behind its own regression run rather than in one large rewrite.

Follow-up

  • How do you test a nightly batch job that has no documentation and no unit tests?
  • During the migration, how do you keep the new service and the mainframe records consistent, and which one wins on a mismatch?
  • When would you decide not to modernise a component at all?

What strategies do you use to manage data consistency in a cloud-native environment?

Medium · Technical and Domain Proficiency

Approach

  1. Start by sorting the data by how consistent it has to be. Balances and ledger entries need atomic, strongly consistent writes inside one service's database. Search indexes, notifications and reporting copies can be eventually consistent. Give each aggregate exactly one owning service.
  2. Avoid dual writes (commit to the database, then publish to the broker), which lose one side when a crash lands in between. Use a transactional outbox: write the event to an outbox table in the same local transaction, then have a relay or change data capture publish it. Delivery is then at-least-once, so consumers must dedupe on an event id.
  3. For workflows that span services, use a saga with compensating actions (orchestrated or choreographed) instead of two-phase commit. 2PC holds locks and blocks if the coordinator fails, and many managed brokers and databases do not support it.
  4. Handle concurrent updates with optimistic concurrency: a version column and UPDATE ... WHERE id = ? AND version = ?; zero affected rows means re-read and retry. Put idempotency keys on client-facing write APIs so a retried request cannot post twice.
  5. Be explicit about replica lag. Route read-your-writes paths to the primary or use session consistency. State the CAP/PACELC trade-off you are making, and run a periodic reconciliation job that detects and reports drift between stores.

Follow-up

  • A consumer applies a message and crashes before acknowledging it. What happens next, and how do you make that safe?
  • Events for the same account arrive out of order. How do you detect it and handle it?
  • How would you find out that two services disagree about the same record?

Walk me through how you would architect a secure, high-availability service for loan processing.

Medium · Problem-Solving and System Design

Approach

  1. Fix the scope first: intake through credit decision, or funding and servicing as well? Then the requirements: request volume, availability target, RPO/RTO, audit and retention rules, and which fields are PII. Model the loan as an explicit state machine (submitted, under review, approved, declined, funded) and persist every transition.
  2. Components: an API gateway with OAuth2/OIDC authentication and role-based authorisation; stateless application services spread across availability zones behind a load balancer; a relational database for loan records (ACID) with a synchronous standby in another zone; an encrypted object store for documents; a queue for calls to external parties such as a credit bureau, with retries, backoff and a circuit breaker.
  3. Security: TLS everywhere, including between services; encryption at rest with keys held in a managed KMS; field-level encryption or tokenisation of government ids and account numbers; secrets kept in a vault; least-privilege service identities; no PII in logs; strict input validation.
  4. Correctness under failure: idempotency keys on submission and disbursement so a retry can never fund a loan twice; optimistic locking on state transitions; outbox events for downstream systems; an append-only audit log of who changed what and when. If the bureau is down, park the application in a pending state instead of failing it.
  5. High availability and DR: multi-zone for zone failures, plus an asynchronous cross-region replica with a stated RPO and a failover runbook that is actually rehearsed. Name the trade-off: synchronous cross-region replication gives zero data loss at the cost of write latency.

Follow-up

  • A whole region goes down mid-day. What happens to applications that were in flight?
  • How do you guarantee a loan is disbursed exactly once when the payment call times out?
  • An auditor asks why a specific application was declined. What does your design let you show them?

How do you approach choosing between a monolithic and a microservices architecture for a new project?

Medium · Problem-Solving and System Design

Approach

  1. Name the deciding factors: how many teams need to deploy independently (Conway's law), whether the domain boundaries are already understood, whether parts of the system have very different scaling or availability needs, and whether CI/CD, observability and on-call are mature enough to run many services.
  2. For a new project with an unclear domain, I default to a modular monolith: one deployable with enforced module boundaries (separate packages, no module reading another module's tables, calls only through module interfaces). That keeps a later extraction cheap.
  3. Count the cost of microservices honestly: network calls that fail and add latency, sagas instead of local transactions, duplicated data and eventual consistency, versioned APIs, distributed tracing and more infrastructure. Against that: independent deploys, independent scaling, fault isolation and a smaller blast radius.
  4. Split on concrete signals: a component with its own load profile, a team repeatedly blocked on another team's release, or a stricter security or compliance boundary, such as isolating the code that handles sensitive personal data to shrink what has to be audited. Extract it with the strangler pattern behind a stable interface.
  5. Watch for the distributed-monolith anti-pattern: services that share one database or have to be deployed together give you the costs of both architectures and the benefits of neither.

Follow-up

  • How would you draw the service boundaries for a lending product, and what would you check before splitting?
  • After the split, a business operation has to update data in two services. How do you keep it correct?
  • What would make you merge two services back together?

Explain your process for debugging a complex performance issue in a distributed system.

Medium · Technical and Domain Proficiency

Approach

  1. Pin the symptom down before theorising: which endpoint or job, which percentile moved (p50 versus p99), since when, and for which callers. Line that timeline up against deploys, config and feature-flag changes, traffic shifts and dependency incidents.
  2. Follow one slow request end to end with distributed tracing (propagated trace ids, for example OpenTelemetry spans) to find the span that dominates the critical path, and tell self-time apart from time spent waiting on a downstream call. Fan-out amplifies the tail: if a request touches 100 backends and each is slow 1% of the time, about 63% of requests hit at least one slow call (1 - 0.99^100).
  3. Check saturation on the suspect hop: CPU, GC pauses, thread and connection-pool exhaustion, database lock waits, queue depth. Little's Law (in-flight = arrival rate x latency) shows when a pool is too small for the current latency.
  4. Test one hypothesis at a time with a change that rules things out whichever way it goes: reproduce under load in a test environment, compare flame graphs or query plans, then confirm the fix on the same percentile before and after.
  5. Name the usual causes: retries without backoff and jitter turning into a retry storm, N+1 remote calls, a missing index or a plan regression, a cache stampede after a mass expiry, a noisy neighbour, and clock skew corrupting a timeline built from log timestamps.

Follow-up

  • p99 doubled but p50 is flat. What does that rule in and rule out?
  • How would you stop a slow dependency from causing a retry storm across the services that call it?
  • How would you confirm the fix in production without rolling it out to all traffic at once?

Suggested practice checklist

The plan starts by fixing your track, then works through the reported technical, design and behavioral questions in that order, and ends with a full mock.

Day 1 — Fix your track and your two project stories

  • Sort every technology in your job description into mainframe, cloud, AI enablement, loan servicing or general application development, and mark the track you will be judged on.
  • Write a two-minute career walkthrough ending on that track, and say it aloud twice.
  • Outline two projects with scale, your decisions, one trade-off and one measurable result each.

Deliverable: A track decision, a written two-minute walkthrough and two project outlines with numbers.

Day 2 — Distributed performance and data consistency

  • Write a debugging sequence for slow requests in a distributed system: metric, trace, saturation and queues, recent changes, one hypothesis at a time, then verification.
  • Write an answer on data consistency in a cloud-native setting covering idempotency keys, optimistic versioning, the outbox pattern, sagas versus transactions, and where eventual consistency is acceptable.

Deliverable: A one-page debugging checklist, a one-page consistency answer and a solved concurrency exercise.

Day 3 — Secure, high-availability loan processing

  • Outline the loan-processing service: clients and entry points, identity, encryption, data stores, queues, audit logging and zone redundancy.
  • Add failure handling: retries with idempotency, timeouts, a circuit breaker and a defined degraded mode.

Deliverable: A component diagram with a failure table for the loan service, a decision rule for architecture style and a solved resilience exercise.

Day 4 — Legacy and mainframe modernization

  • Write an answer on keeping legacy code maintainable and scalable: characterization tests, an API layer over stable batch jobs, small refactors behind it, and rollback per step.
  • Prepare a mainframe interoperability example covering how a modern service reads from or writes to a legacy system without breaking its processes.

Deliverable: A written modernization answer, one interoperability example and a solved comparison exercise.

Day 5 — AI/ML in production and pivots

  • Write the AI/ML integration story as a rollout ladder: shadow mode, small slice, full release, fallback, monitoring for drift and latency, and a retraining trigger.
  • Write the pivot story using a real constraint, the assumption that failed, two alternatives and the measured outcome.
  • Add a security paragraph to the AI story about access to features, encryption and prediction logging.

Deliverable: Two written stories, each with a decision, a rejected alternative and a result.

Day 6 — Stakeholders, disagreement and ownership

  • Write the stakeholder trade-off story with options in terms of date, cost and risk, plus your recommendation.
  • Write the disagreement story with the criteria and evidence you used and how it was settled.
  • Write the stalled-project story with the first moves, the scope you cut and the outcome.

Deliverable: Three written stories with outcomes, each cut to about two minutes when spoken.

Day 7 — Full mock of both stages

  • Give the screening pitch aloud, then answer the loan-processing design from a blank page.
  • Answer the debugging, consistency and legacy questions aloud, then two behavioral prompts.
  • Review your answers and mark where security, failure modes or a result were missing, then fix those answers.

Deliverable: A recorded mock with a list of gaps and the revised answers for each gap.

Stories to rehearse for the BMO loop

Candidates report that the behavioral side covers working with product managers, security teams and operations, and handling technical disagreement inside a large financial organization. The reported prompts ask for a pivot, an explanation to a non-technical stakeholder, a disagreement, a stalled project and AI/ML integration. Each story should show a decision you made, the alternative you rejected and a measurable result.

  1. Tell me about a time a security or compliance requirement changed your design.

Describe your experience integrating AI/ML models into existing production workflows.

Medium · Technical and Domain Proficiency

Approach

  1. Pick one real case where a model went into a workflow that already existed, and name the workflow, what the model decided or predicted, and how it was called (batch scoring, request-time service or queue consumer). State which part you owned, such as the serving wrapper, the data pipeline or the rollout.
  2. Walk through the rollout as a risk ladder: run the model in shadow mode beside the existing logic, compare outputs, release to a small slice, then widen. Mention the fallback path and the switch that restores the previous behavior if the model fails or slows down.
  3. Cover the lifecycle after launch: a versioned model artifact, validation against a holdout set before release, monitoring for latency, input drift and shifts in the output distribution, and a defined trigger for retraining or rollback.
  4. If your data was sensitive, say how you handled access control, encryption and logging of predictions so decisions could be reviewed later. For a bank-style setting this shows you think about governance as part of the integration.
  5. If your ML exposure is limited, use an adjacent case such as wrapping a third-party model API with timeouts, retries, caching and monitoring, and be exact about what you built. The mistake that sinks this answer is describing model accuracy only and saying nothing about integration, rollout or monitoring.

Follow-up

  • How did you know the model was behaving well a month after launch?
  • What happened to the workflow when the model service was slow or down?
  • How did you detect and handle drift in the input data?

Describe a time you had to pivot your technical approach due to unforeseen constraints.

Medium · Problem-Solving and System Design

Approach

  1. Choose a pivot caused by a specific constraint you discovered after work began, such as a latency budget, a rate limit, data volume, a vendor restriction or a security requirement. Name the original plan and the assumption that turned out wrong, in two or three sentences.
  2. Show how you found the constraint: a measurement, a failed test, a review comment. Then lay out two or three alternatives with their costs in time, risk and complexity, and say why you picked one.
  3. Describe how you reset the plan with the people depending on it: what you told them, what scope or date changed, and who agreed. A pivot you made alone and announced late is a weak story.
  4. Close with the measured result and a lesson that changes behavior, for example a spike or load test you now run before committing to a design. Avoid blaming bad luck. The strongest version admits which assumption you should have tested earlier.

Follow-up

  • What would you have checked earlier to avoid the pivot?
  • How did you communicate the change to the teams depending on your original plan?
  • What did you give up by choosing the new approach?

Tell me about a time you had to explain a complex technical trade-off to a non-technical stakeholder.

Medium · Behavioral and Leadership

Approach

  1. Pick a trade-off where both options were reasonable, such as shipping with a manual step now versus waiting for full automation, or paying down a risky component versus building a requested feature. Name the stakeholder and what they cared about.
  2. Translate each option into the stakeholder's terms: delivery date, cost, customer impact, operational risk and compliance exposure. Use at most one analogy and drop the jargon, but keep the facts intact.
  3. State your recommendation and what it costs, instead of presenting a neutral menu. Say what you would monitor so the stakeholder can see if the risk you accepted shows up.
  4. Show how you checked understanding, such as asking them to restate the decision or sending a short written summary, and give the outcome. The mistake to avoid is a story where you simplified until the answer was vague, or one where you only lectured on technical detail.

Follow-up

  • What did the stakeholder decide, and did you agree with it?
  • How would you have explained it if they had pushed back on the deadline?
  • How did you confirm they had understood the risk?

How do you handle disagreements within your engineering team regarding technical direction?

Medium · Behavioral and Leadership

Approach

  1. Since the question asks how you handle it, open with your method in two sentences: listen until you can state the other position fairly, turn opinions into criteria, and settle the choice with evidence. Then support it with one concrete example.
  2. In the example, name the technical choice at issue, such as a framework, a data model or a service boundary. Show how you turned it into a comparison: agreed criteria (latency, operability, team skill, migration cost), a benchmark or short spike, and perhaps a written decision record.
  3. Say how it was resolved. If the group still split, describe escalating to the person who owns the decision, then committing fully to the outcome. If you were overruled, say what you did to make the chosen path succeed.
  4. Show the relationship result as well as the technical one. Avoid a story in which you were simply right and the others were wrong, and avoid one where you avoided the conflict entirely.

Follow-up

  • What did you do when you still believed the team's choice was wrong?
  • How would you handle the same disagreement with a more senior engineer?
  • Tell me about a time the evidence changed your own mind.

Describe a situation where you took ownership of a project that was failing or stalled.

Medium · Behavioral and Leadership

Approach

  1. Describe the state you inherited and why it had stalled: no clear owner, an unresolved dependency, shifting scope or an unreliable estimate. Say why you took it on and what authority you had or sought from your manager.
  2. Lay out your first moves in order: talk to everyone involved, list what is actually done and what remains, and cut the remaining work to a milestone that can ship. Then agree owners, a short check-in cadence and a visible status.
  3. Name the blockers you escalated and how you made the ask specific, with the decision needed, the options and the date it mattered. Mention scope you cut and who agreed to the cut.
  4. Finish with the result in measurable terms, such as the milestone met, the defect rate or the date delivered, and what you set up so the project would not stall again. The mistake to avoid is a solo-hero story with no delegation and no stakeholder agreement.

Follow-up

  • How did you get the authority to change the plan?
  • What did you cut, and who had to agree to cutting it?
  • What would you put in place at the start to prevent the same stall?