cswork / libraryOpen practice workspace

Interview guides · Fustis

Fustis Software Engineer Interview Guide

Fustis Software Engineer interview guide: reported rounds, questions and how candidates describe the process.

The 60-second brief

As a Software Engineer at Fustis, you are at the heart of our mission to deliver robust, scalable, and secure infrastructure solutions. This role is not merely about writing code; it is about architecting the systems that power our clients' most critical operations, from cloud-native migrations and network security to complex application hosting and integration projects. Your work directly dictates the performance, stability, and security posture of the environments we support. You will operate in a dynamic, high-stakes environment where technical precision is paramount. Whether you are working on AWS cloud architecture, DevSecOps pipelines, or legacy system modernization, your impact is immediate and measurable. We look for engineers who possess both the depth to solve complex technical hurdles and the breadth to understand how their solutions fit into the broader business ecosystem.

This guide is scoped to a Software Engineer candidate at Fustis.

Focus areas

  • Cloud Engineering
  • AWS
  • DevSecOps

The Fustis interview process

Fustis candidates report 2 rounds over 2-4 weeks. The stages below are what candidates describe, not a published process.

Initial Screening

A preliminary assessment to gauge your background and fit for the role.

What to demonstrate

  • A preliminary assessment to gauge your background and fit for the role
  • Depth in Cloud Engineering

How to prepare

  • Be able to walk your CV end to end in two minutes, and say why this company specifically.
  • Have your salary expectations, notice period and location constraints ready, and ask for the rest of the loop in writing.

Technical Assessments

One or more technical assessments or deep-dive interviews with senior engineers or team leads.

What to demonstrate

  • One or more technical assessments or deep-dive interviews with senior engineers or team leads
  • Depth in Cloud Engineering

How to prepare

  • Answer aloud and timed: What are the primary security considerations when implementing a DevSecOps pipeline in a regulated environment?
  • Answer aloud and timed: How do you handle state management in a Cloud-native backend service?

Where candidates lose points

Own your failures

If asked about a past mistake, focus on what you learned and how you changed your process to prevent it from recurring.

Be clear about your stack

If you are a Java expert, don't be afraid to lean into that, but show you are capable of learning new tools.

Prepare your questions

Your questions for the interviewer are as important as their questions for you. Ask about team culture, project challenges, and the roadmap for the team.

Questions & practice

Describe the differences between synchronous and asynchronous processing in your preferred programming languag

Medium · Technical Domain Expertise

Describe the differences between synchronous and asynchronous processing in your preferred programming language.

Approach

  1. Pick one language and answer concretely, e.g. Python. Synchronous code runs each call to completion and blocks the thread while it waits on I/O. Asynchronous code lets a task suspend while it waits, so the same thread runs other work and resumes the task when its result is ready.
  2. async def defines a coroutine function; calling it returns a coroutine object. await yields to the event loop only when the awaited object must wait (a socket read, asyncio.sleep); awaiting a coroutine that never waits runs straight through. One thread interleaves them, so nothing runs in parallel.
  3. Say when each wins: async suits very many concurrent I/O-bound tasks at little memory each. Sync code gets I/O concurrency from a ThreadPoolExecutor: CPython releases the GIL during blocking I/O, so threads overlap waits well at tens to hundreds of connections. Plain sync code is easiest to debug.
  4. Handle CPU-bound work: neither model speeds it up, since in default CPython the GIL stops threads running bytecode in parallel; use multiprocessing or a ProcessPoolExecutor. From async code, send CPU work to run_in_executor with a process pool; asyncio.to_thread suits blocking I/O, not CPU work.
  5. Name the classic bugs: a blocking call inside a coroutine (time.sleep, requests.get, a sync DB driver) stalls the event loop and every request on it; a coroutine called without await never runs (Python warns it was never awaited); and async spreads up the call stack.
  6. Contrast briefly: JavaScript has one event loop with Promises, Java 21 adds virtual threads, Go multiplexes goroutines onto OS threads. Between services, async means queueing work (SQS, Kafka) and returning 202 with a job ID to poll, gaining retries and load buffering at the cost of eventual consistency.

Follow-up

  • How would you make ten HTTP calls concurrently in Python? Use an async client such as httpx.AsyncClient or aiohttp, run them with asyncio.gather, and cap concurrency with an asyncio.Semaphore.
  • Concurrency versus parallelism? Concurrency means tasks make progress in overlapping time and parallelism means executing at the same instant on multiple cores, so asyncio gives concurrency while processes give parallelism.
  • How do you handle errors and timeouts in async code? Use a TaskGroup (3.11+) or gather(..., return_exceptions=True) for sibling failures, and bound every await with asyncio.timeout (3.11+) or asyncio.wait_for.

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?

Identify the heaviest tenants in a five-minute window under memory pressure

Medium · top-k · heavy hitters · streaming

The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.

Approach

  1. Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
  2. Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
  3. State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
  4. Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.

Follow-up

  • The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
  • Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?

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

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

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?

How do you optimize AWS resource utilization to reduce costs without compromising performance?

Medium · Technical Domain Expertise

Approach

  1. Start with visibility, not cuts: turn on Cost Explorer and the Cost and Usage Report, enforce cost-allocation tags (team, service, environment) and set AWS Budgets alerts. Record a performance baseline per service (p95 latency, error rate, saturation) so every saving can be checked against it.
  2. Rightsize, then make capacity follow demand: use Compute Optimizer and CloudWatch (agent for memory) to downsize underused instances a step at a time, move to Graviton where the stack has arm64 builds (benchmark first), run target-tracking Auto Scaling, and stop dev and test outside working hours.
  3. Match the pricing model to the load shape: Savings Plans or Reserved Instances for the steady baseline, Spot for interruptible stateless work (batch, CI, fault-tolerant fleet capacity), on-demand for the spiky remainder. Commit only after rightsizing, or you lock in the waste.
  4. Clean up storage: delete unattached EBS volumes and stale snapshots, move gp2 to gp3, and tier S3 to IA or Glacier only for rarely read data, as minimum storage durations (30 to 180 days), transition charges and retrieval fees can raise the bill. Use Intelligent-Tiering when access is unknown.
  5. Cut network and idle charges: NAT Gateway processing (add free gateway endpoints for S3 and DynamoDB), cross-AZ transfer, idle load balancers, and public IPv4 addresses: since February 2024 AWS bills every one hourly, in use or idle, not only unattached Elastic IPs.
  6. Protect performance explicitly: roll changes out gradually, watch SLO dashboards and alarms, and keep enough headroom to survive losing an AZ. The weak answer is 'buy Reserved Instances' or 'shrink everything'; the strong one ties each saving to a metric proving nothing regressed.

Follow-up

  • Savings Plans or Reserved Instances? Prefer Compute Savings Plans, which cover any instance family, size and region plus Fargate and Lambda, unless usage is very stable or you need the capacity reservation that zonal RIs add.
  • How do you run production on Spot safely? Diversify instance types and AZs with the price-capacity-optimized allocation strategy, keep an on-demand base, and drain on the rebalance recommendation or the two-minute interruption notice.
  • How do you stop costs creeping back? Enforce tagging by policy, show each team its own spend, turn on Cost Anomaly Detection, and review rightsizing recommendations on a fixed cadence.

What are the primary security considerations when implementing a DevSecOps pipeline in a regulated environment

Medium · Technical Domain Expertise

What are the primary security considerations when implementing a DevSecOps pipeline in a regulated environment?

Approach

  1. Frame two goals: stop vulnerable or tampered code reaching production, and prove it to an auditor. Under PCI DSS or SOX, a pipeline that deploys to in-scope systems is itself in audit scope, and SOC 2 attestations test change management too, so controls must be enforced and evidenced.
  2. Treat the pipeline as a production system: least-privilege CI identities, short-lived cloud credentials via OIDC federation instead of stored keys, secrets in a vault or secrets manager and masked in logs, protected branches with required review, and ephemeral, isolated build runners.
  3. Layer automated checks by stage: secret scanning and SAST on every commit, SCA and license checks on dependencies, IaC scanning for Terraform or CloudFormation, container image scanning, and DAST against staging. Define which severities block the build and a time-boxed, approved exception process.
  4. Secure the supply chain: pin dependencies (hashed lockfiles) and base images by digest, generate an SBOM per build, and sign artifacts (e.g. cosign) with SLSA provenance. Verify signatures at cluster admission (e.g. Kyverno), not only in CI, so images that skipped the pipeline are refused.
  5. Enforce separation of duties: authors cannot approve their own changes, production deploys need a recorded approval, and nobody holds standing production access (break-glass access is logged and reviewed). Keep immutable, retained logs linking commit, reviewer, scan results and deployment.
  6. Cover data and runtime too: no unmasked production data in lower environments, encryption in transit and at rest with managed keys, policy-as-code guardrails (e.g. OPA) and runtime monitoring. For every gate, be ready to say what evidence it produces and who can bypass it.

Follow-up

  • How do you keep security gates from blocking every release? Tune rules to cut false positives, fail only on new high or critical findings, triage the existing backlog separately, and surface issues early in the PR.
  • A critical CVE is found in a dependency already in production: what now? Use SBOMs to find affected services, assess exploitability, patch or mitigate through the normal pipeline with expedited approval, and record it.
  • How do you stop a malicious or typosquatted package entering the build? Resolve dependencies only through an internal proxy registry, block unvetted new packages by policy, and review each new direct dependency like a code change.

How do you handle state management in a Cloud-native backend service?

Medium · Technical Domain Expertise

Approach

  1. Default to stateless instances: no session or request state in process memory or on local disk, so any replica can serve any request and pods can be killed, rescheduled or autoscaled freely. The real skill is deciding where state goes instead.
  2. Put each kind of state in a store built for it: business data in a managed database (PostgreSQL, DynamoDB), sessions and cache in Redis with TTLs, files in S3, in-flight work in a queue or log (SQS, Kafka). Signing a client-side token stops tampering, not reading: keep secrets out or encrypt it.
  3. Handle concurrency explicitly, since many replicas write at once: optimistic locking with a version column or conditional writes, database transactions for invariants, and idempotency keys on write APIs so client retries do not apply twice. In-memory locks protect only one instance.
  4. For workflows spanning services, use a saga (event choreography or an orchestrator) with compensating actions instead of distributed transactions, and the transactional outbox pattern so a database write and its published event cannot diverge.
  5. Name the caching tradeoff: cache-aside with TTLs is simple but serves stale reads, so invalidate on write where staleness matters. Sticky sessions are a crutch that breaks on scale-in and skews load, and per-instance local caches drift apart across replicas.
  6. When a component must own state (a database, a stream processor), prefer the managed service or run it as a Kubernetes StatefulSet with stable identity and persistent volumes. Say how state survives losing an AZ: replication, backups and restores you have actually tested.

Follow-up

  • How do you implement idempotency keys? Claim the key and a request hash under a unique constraint, commit the response together with the write, replay it on repeats, reject a changed payload (422) and return 409 while the first is in flight.
  • JWT or server-side sessions? JWTs skip a lookup but cannot easily be revoked before expiry while Redis-backed sessions revoke instantly, so short-lived access tokens plus refresh tokens are the usual compromise.
  • What can go wrong with the outbox pattern? The relay may publish an event more than once, so consumers must be idempotent; you also need to monitor relay lag and clean up published rows.

Walk me through how you would architect a highly available hosting solution for a mission-critical application

Medium · Problem-Solving & Architecture

Walk me through how you would architect a highly available hosting solution for a mission-critical application.

Approach

  1. Pin down targets first, stated as assumptions: e.g. 99.95% availability (about 22 minutes of downtime a month), RTO under 15 minutes, RPO near zero, a web workload with daily peaks. These numbers decide whether multi-AZ is enough or multi-region is required.
  2. Baseline in one region across three AZs: DNS (Route 53) to a CDN and WAF, then a load balancer with health checks, then an autoscaled group of stateless app instances or containers spread across AZs, with minimum capacity sized so losing one AZ still carries peak load.
  3. Make the data tier survive failure: a managed database with synchronous cross-AZ replication and automatic failover (e.g. RDS Multi-AZ, or Aurora with a replica in another AZ), replicated Redis for sessions and cache, object storage for files, and point-in-time backups with tested restores.
  4. Choose multi-region by RTO and RPO: warm standby (a scaled-down stack plus async cross-region replication) or active-active. Async replication means a regional failover can lose seconds of writes, and active-active needs write conflict handling, so justify the choice.
  5. Remove the less obvious single points of failure: one NAT gateway per AZ, replicated secrets and config, no hidden dependency on a single AZ, and blue/green or canary deploys with automatic rollback, because bad changes cause more outages than hardware.
  6. Prove it works: keep load-balancer health checks shallow, as a deep check on a shared database can fail every instance at once; probe dependencies separately for alerting and failover. Add SLO alerts, runbooks and failover drills, plus timeouts, circuit breakers and load shedding against gray failures.

Follow-up

  • How does DNS failover affect RTO? Resolvers and clients cache records for the TTL and some ignore it, so keep TTLs low (e.g. 60s) or use a global anycast load balancer for faster, more reliable cutover.
  • How do you avoid split-brain during database failover? Rely on a managed service or quorum-based consensus that fences the old primary before promoting a replica, so two nodes never accept writes at once.
  • How do you make schema changes without downtime? Expand and contract: add new columns or tables, deploy code writing both, backfill, switch reads, then drop the old structure in a later release.

How do you approach the migration of a legacy application to a modern cloud environment?

Medium · Problem-Solving & Architecture

Approach

  1. Start with discovery and a goal: inventory components, dependencies, data stores, integrations, hard-coded IPs and licensing, and agree on why you are migrating (cost, scale, end-of-life hardware, delivery speed). The goal decides how much to change.
  2. Choose a strategy per component using the 7 Rs: retire, retain, rehost (lift and shift), relocate, replatform (e.g. move the database to a managed service), refactor, or repurchase as SaaS. Rehosting is fastest and lowest risk; refactoring unlocks the most but costs the most.
  3. Before any workload moves, build the landing zone: account structure, networking (VPN or Direct Connect for the hybrid phase), IAM, central logging and infrastructure as code, so every migrated workload inherits security and repeatability. Make the app deployable by pipeline, not by hand.
  4. Migrate incrementally: rehosted and replatformed components move in waves of coupled applications. Where you refactor, use the strangler fig pattern: a proxy or API gateway in front of the legacy system, one capability moved behind it at a time. A big-bang cutover puts all risk on one date.
  5. Treat data migration as the hardest part: a bulk load plus change data capture (e.g. AWS DMS or Debezium) that keeps the target in sync, reconciliation on row counts and checksums, and a short cutover window. Decide the rollback path before you cut over.
  6. Validate and cut over safely: load tests against the pre-migration baseline, parallel runs or shadow traffic where feasible, a rehearsed runbook with go/no-go criteria, and close monitoring afterwards. Keep the old environment runnable until the new one has been stable for an agreed period.

Follow-up

  • How do you migrate the database with minimal downtime? Start CDC before the snapshot or from its LSN or binlog position, apply changes until lag nears zero, freeze writes briefly, verify, switch, and keep reverse replication for rollback.
  • How do you handle latency while some components stay on-premises? Count network round trips per request, keep chatty pairs together on one side, and batch or cache calls that must cross the VPN or Direct Connect link.
  • When would you not refactor? When the app is stable, rarely changed or near retirement: rehost or replatform it, and spend refactoring effort only where change frequency or scaling needs justify it.

Explain your approach to debugging a critical failure in a production MuleSoft integration.

Medium · Technical Domain Expertise

Approach

  1. Scope the impact before touching anything: which flows or APIs fail, since when, for every message or a subset, and whether data is lost or only delayed. Check app and worker status in Runtime Manager, and match the start time to deployments, property or certificate changes, and upstream releases.
  2. Follow one failing transaction end to end by its correlation ID through the Mule logs and the systems on each side. Let the error type steer you: HTTP:CONNECTIVITY or HTTP:TIMEOUT points outward, MULE:EXPRESSION usually means a DataWeave failure, HTTP:UNAUTHORIZED points at credentials.
  3. Rank the likely causes: a downstream system that is down, slow or changed (schema, auth, rate limits); an expired TLS certificate or rotated secret; a new payload shape breaking a DataWeave transform; heap, thread or vCore exhaustion under a volume spike; a recent deployment.
  4. Mitigate before root-causing: roll back if a deployment lines up, stop or throttle the source (scheduler, listener, queue consumer) so failures stop piling up, restart or scale workers only as a stopgap, and make sure failed messages land somewhere replayable, such as a dead-letter queue.
  5. Confirm the fix by replaying a captured failing payload in a lower environment, then reprocess the backlog in controlled batches. Check that targets are idempotent or deduplicate on a business key first, because replay can create duplicate orders or records.
  6. Close with prevention: alerts on error rate and queue depth, reconnection strategies and bounded retries on connectors, On Error Propagate versus On Error Continue chosen deliberately per flow, and a blameless post-incident review that records the timeline and root cause.

Follow-up

  • On Error Propagate or On Error Continue? Propagate runs the handler then rethrows, so the caller sees a failure and transactions roll back; Continue treats the error as handled and returns the handler's result as success.
  • What if the logs show nothing useful? Raise the log level for the relevant connector package, log the correlation ID and key fields at every flow boundary, and capture the raw request and response at the edge.
  • How do you prevent duplicates when replaying? Use the Idempotent Message Validator or an Object Store keyed by a business ID, and make downstream writes upserts rather than inserts.

Suggested practice checklist

Built from the rounds and topics Fustis candidates report.

Day 1 — Map the Fustis loop

  • Write out the reported sequence: Initial Screening, Technical Assessments.
  • For each round, write one sentence on what it is judging, from the description above, and mark the one you are least ready for.

Deliverable: A one-page map of the 2 reported rounds, with the weakest marked.

Day 2 — Work Cloud Engineering

  • Spend the session on Cloud Engineering, which Fustis candidates report being tested on.
  • Write one worked example in Cloud Engineering and time yourself on it.

Deliverable: One timed worked example in Cloud Engineering.

Day 3 — Work AWS

  • Spend the session on AWS, which Fustis candidates report being tested on.
  • Write one worked example in AWS and time yourself on it.

Deliverable: One timed worked example in AWS.

Day 4 — Work DevSecOps

  • Spend the session on DevSecOps, which Fustis candidates report being tested on.
  • Write one worked example in DevSecOps and time yourself on it.

Deliverable: One timed worked example in DevSecOps.

Day 5 — Answer out loud: Technical Domain Expertise

  • Answer aloud, timed: How do you optimize AWS resource utilization to reduce costs without compromising performance?
  • Answer aloud, timed: Explain your approach to debugging a critical failure in a production MuleSoft integration.

Deliverable: Spoken answers to 2 reported Technical Domain Expertise question(s), under time.

Day 6 — Answer out loud: Problem-Solving & Architecture

  • Answer aloud, timed: Walk me through how you would architect a highly available hosting solution for a mission-critical application.
  • Answer aloud, timed: How do you approach the migration of a legacy application to a modern cloud environment?

Deliverable: Spoken answers to 2 reported Problem-Solving & Architecture question(s), under time.

Day 7 — Answer out loud: Behavioral & Situational

  • Answer aloud, timed: Tell me about a time you had to explain a complex technical trade-off to a non-technical stakeholder.
  • Answer aloud, timed: How do you stay updated with rapidly evolving technologies like AI or new Cloud services?

Deliverable: Spoken answers to 2 reported Behavioral & Situational question(s), under time.

Behavioural preparation

Behavioural rounds judge the decision you made and what it cost.

  1. How do you handle state management in a Cloud-native backend service?

Describe a time you identified a bottleneck in a network or application flow; what steps did you take to resol

Medium · Problem-Solving & Architecture

Describe a time you identified a bottleneck in a network or application flow; what steps did you take to resolve it?

Approach

  1. Choose a story where measurement, not a guess, led you to the bottleneck: you took a baseline before changing anything and can reason about latency, throughput and saturation. A lucky guess that happened to work leaves you nothing to show when asked how you found it.
  2. Open with the symptom and its cost in numbers, e.g. 'p99 checkout latency rose from 300 ms to 4 s at peak and timeouts failed about 2% of orders.' Say what your role was and why the problem landed with you.
  3. Walk through how you localized it: the metrics, traces, profiles or packet captures you used, the hypotheses you ruled out, and the evidence for the real constraint (e.g. connection pool exhaustion, an N+1 query, a missing index, TLS handshake overhead, an MTU mismatch).
  4. Explain the fix and the tradeoff: a fast mitigation first (scaling out, raising a limit, a cache) versus the root fix (query rewrite, batching, moving work async), and how you checked you had not simply moved the bottleneck downstream.
  5. Quantify the result on the same metric you opened with (latency, throughput, errors, cost), then close with what you made durable: an alert, a load test in CI, or a design guideline, so the problem does not come back.

Follow-up

  • How did you know it was the true bottleneck and not a symptom? Show that relieving it moved the end-to-end metric, and that this component was saturated or queueing while stages around it had slack.
  • How would you find a bottleneck in a system you have never seen? Start from the user-facing metric, follow a request with tracing, and apply the USE method (utilization, saturation, errors) to each resource on the path.
  • Network or application: how did you tell which it was? Compare server-side processing time with end-to-end latency; a large gap points outside the app (DNS, TLS handshakes, retransmits, MTU), a small one at the app or its dependencies.

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

Medium · Behavioral & Situational

Approach

  1. Pick a story with a real tradeoff and consequences, where you turned a technical choice into a business decision the stakeholder could actually make, without condescending or hiding risk. Explaining what an API is does not involve a tradeoff.
  2. Set up the tension concretely: who the stakeholder was, what they cared about (launch date, cost, customer impact, compliance) and the options, e.g. 'ship in 3 weeks on the current database with a known scaling ceiling, or in 7 weeks with a redesign that removes it.'
  3. Show the translation: options framed in their terms (time, money, risk, customer experience), each risk with rough likelihood and impact, an analogy only if it genuinely clarifies, and your recommendation stated plainly while leaving the decision to them.
  4. Include friction: pushback, a question you could not answer yet, or a decision against your advice, and how you handled it. A story where everyone instantly agreed shows little, and blaming the stakeholder for not understanding backfires.
  5. End with the outcome and proof it landed: what was decided, whether the tradeoff played out as predicted, and a habit you kept, e.g. writing decisions down with their accepted risks so they are not relitigated later.

Follow-up

  • What if they chose the option you advised against? Record the decision and the accepted risk, commit to making it work, and agree a trigger (a metric or date) for revisiting it.
  • How do you check they really understood? Ask them to restate the choice and its consequences in their own words, or what would make them change their mind.
  • How do you communicate an uncertain estimate? Give a range with the main risk drivers rather than a single date, and say what you will know by when to narrow it.

How do you stay updated with rapidly evolving technologies like AI or new Cloud services?

Easy · Behavioral & Situational

Approach

  1. Frame the answer around how you filter and apply, not how much you read: a habit for separating hype from useful change, plus one example of putting something new to work. A bare list of newsletters and podcasts says nothing about judgment.
  2. Describe sources by type, briefly: primary sources (release notes, official docs and changelogs for services you use, papers or model cards for AI), a few trusted practitioner channels, and colleagues or communities where you debate them. Name the ones you really use.
  3. Show how you get hands-on: small prototypes, a sandbox account with budget alerts, or a certification when you wanted structured depth, e.g. 'I rebuilt a small internal tool on a new managed service to compare its cost and latency with our current setup.'
  4. Explain your adoption filter: does it solve a problem we actually have, how mature is it (GA or preview, support, pricing, lock-in), what is its security and data-handling story (critical for AI tools), and how could we trial it cheaply before committing?
  5. Give one concrete case where learning changed something at work, with the outcome: a tool adopted, a cost cut, or something evaluated and rejected with clear reasons. A well-argued rejection of a hyped tool works as well as an adoption story.

Follow-up

  • How do you find time for it? Set a fixed weekly budget, skim widely, and go deep only on what touches your current or upcoming work; the rest can wait until it becomes relevant.
  • How would you evaluate an AI coding assistant before using it on real work? Check data retention and licensing terms, trial it on representative tasks under code review, measure quality and time saved, and keep humans accountable.
  • How do you share what you learn? Write short notes or give demos, and turn a promising tool into a design proposal so others can critique it before adoption.

Describe a situation where you had to lead a project with limited documentation or incomplete requirements.

Medium · Behavioral & Situational

Approach

  1. Frame the story around creating clarity nobody handed you: finding the real goal, acting on partial information and keeping people aligned. Show what you pinned down immediately and what you deliberately left open, rather than waiting for complete requirements.
  2. Set up the ambiguity specifically: what was missing (no documentation for a legacy system, a one-sentence goal, conflicting stakeholder asks), the stakes and deadline, and why you were the one leading it.
  3. Show how you reduced uncertainty: interviewing stakeholders and users, reading code, logs and data to reverse-engineer behavior, and writing a one-page scope with assumptions and open questions that stakeholders signed off. Name your riskiest assumption and how you tested it.
  4. Show execution choices: a thin end-to-end first version to get feedback early, time-boxed spikes on unknowns, a decision log, and how you handled a requirement that changed midway (an explicit re-scoping conversation, not silently absorbing it).
  5. Close with measurable outcomes (delivered when, adoption, defects, time saved) and what you left behind, such as the documentation you wrote so the next person does not face the same gap. Own one thing you would do differently.

Follow-up

  • How did you decide you knew enough to start? Resolve the decisions that are expensive to reverse first; for cheap-to-change ones, proceed on written assumptions and revisit them.
  • What if two stakeholders wanted conflicting things? Make the conflict explicit in writing, tie each ask to the business goal, and take it to whoever owns the priority rather than choosing silently.
  • How did you keep the team aligned as things changed? Keep one living scope document or decision log, hold short regular check-ins, and name an owner for every open question.