Interview guides · Alkami Technology
Alkami Technology Software Engineer Interview Guide
Alkami Technology Software Engineer interview guide: reported questions on distributed systems, testing, performance and security, plus a 7-day plan.
The 60-second brief
Alkami Technology builds a cloud-based digital banking platform that financial institutions offer to their own customers. Candidate reports also mention modules associated with MANTL, alongside work on core banking infrastructure. A Software Engineer there turns business requirements into services and features, and works with product managers, other engineering teams and DevOps so the code reaches production in a form that stays fast, reliable and secure. Because the product moves money and account data, questions about availability, consistency and security are the natural lens for this loop.
This guide is for candidates interviewing for a Software Engineer position at Alkami Technology. It covers the reported topic areas (distributed systems and consistency, performance tuning, security by design, testing and refactoring, client-side state), the stack signals candidates mention (cloud platforms and microservices), and the behavioral material that goes with them. Candidates report no fixed round sequence, so the guide prepares you by topic and tells you what to confirm with your recruiter.
Candidates report a screening call about your background, followed by technical deep-dives and behavioral assessments. No round names, order or durations are reported, so prepare by topic and confirm the format with your recruiter.
Focus areas
- Technical and Domain Proficiency
- Architecture and Scalability
- Code Quality and Maintainability
Overview
The reported question set is broad rather than puzzle-shaped. It opens with five direct questions: high availability and fault tolerance in a distributed system, the trade-offs between database consistency models, testing in a microservices architecture, optimizing a slow API endpoint, and managing state in complex client-side applications. The remaining reported items are topic areas: CAP and consistency, performance tuning, security by design, refactoring, testing paradigms and giving peers useful feedback. Treat each as a prompt for a two-to-three minute spoken answer that names a mechanism, a trade-off and a failure case.
The product is digital banking delivered through a cloud platform, so the strongest answers tie general principles to banking-shaped examples. A funds transfer and an account activity feed need different consistency choices. A retry after a timeout must not move money twice. A security answer has to cover who can call an endpoint, what is logged and where secrets live. Prepare one or two such examples and reuse them across the availability, consistency and security answers.
Candidates describe the day-to-day work as sprint planning, design sessions, code reviews and close work with product managers and DevOps, often on ambiguous problems where you research, prototype and present a recommendation. That points to two kinds of preparation: stories from your own work (a production incident, a stretch of technical debt, a slow endpoint you fixed, a review where you gave feedback), and practice explaining technical trade-offs to someone who is not an engineer.
Reports suggest applied engineering matters more than theoretical algorithm work, and they do not describe a live coding stage. Confirm with your recruiter whether you will write code, and keep one language and one framework you can discuss in depth, since candidates describe questions that go into the reasons behind a technical decision in your own stack.
Where to focus your preparation
Candidates report a screening call, then technical deep-dives and behavioral assessments, but no named rounds or order. The single entry below is a preparation focus covering all of it.
Preparation focus
No round names or sequence are reported for this loop. Candidates describe a screening call about your background and interest in the company, followed by technical deep-dives and behavioral assessments, with engineers and managers involved. Use the reported questions as your curriculum: availability and fault tolerance, consistency models and CAP, microservice testing, performance tuning, security by design, refactoring and client-side state, plus stories about incidents, technical debt and peer feedback. Ask your recruiter for the stage list, who you will meet and whether you will write code or draw a design, then tailor the last days of preparation to the answer.
What to demonstrate
- Depth in distributed-systems topics: availability, fault tolerance, consistency models and CAP
- Ability to reason about performance, security and testing as parts of one system
- Experience handling production incidents and technical debt, told through your own examples
- Clarity when explaining technical decisions and giving or taking feedback
How to prepare
- Write a spoken answer for each of the five direct questions, each with one banking-flavored example such as a funds transfer or a balance read
- Prepare two stories, one production incident and one piece of technical debt, each with your decision, the trade-off and the result
- Choose one language and framework from your own experience and be ready to explain why you made the choices you did in it
- Send your recruiter a short list of format questions: stages, interviewers, live coding or not, and design discussion style
Where candidates lose points
Reciting CAP as "pick two of three" without tying it to a real operation
Anchor the theorem in a concrete case. For a funds transfer, say you prefer rejecting or delaying requests during a partition over accepting conflicting writes. For an activity feed or notification list, say stale reads are acceptable. Then name the consistency level you would configure and what the user sees during the failure.
Answering the slow-endpoint question with fixes before measurement
Tell the story in order: the symptom and its percentile (p95 or p99, not the average), how you profiled or traced it, the single bottleneck you found (N+1 queries, a missing index, a slow downstream call, oversized payload), the one change you made, and the before and after numbers.
Describing microservice testing as a generic test pyramid
Name what is specific to services: consumer-driven contract tests between services, test doubles or sandboxes for dependencies, a small number of end-to-end paths through the real deployment, and fault-injection checks for timeouts and retries. Say what you deliberately leave out of end-to-end tests and why.
Treating security by design as a review step at the end
Walk through the lifecycle: threat modelling at design time, authentication and authorization on every endpoint, input validation, secrets kept out of code, dependency scanning and tests in CI, audit logging for sensitive actions, and what you check during code review.
Telling an incident or technical-debt story where the team made every decision
Pick a story where you made a specific call. State the situation, what you decided and what you rejected, how you handled the risk (a rollback path, a feature flag, a staged rollout), the result, and what you changed afterwards, such as a runbook, alert or follow-up refactor.
Questions & practice
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
- 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.
- 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.
- 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.
- 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?
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
- 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.
- 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.
- 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.
- 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?
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?
Explain why the owner filter ignores the listing index
Medium · composite index · query plans · sort elimination
The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.
Approach
- Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
- For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
- Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
- Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.
Follow-up
- 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
- A dashboard runs this for 40 owners in one page load. What changes about the design?
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?
How do you ensure high availability and fault tolerance in a distributed system?
Medium · Technical and Domain Proficiency
Approach
- Start from targets, not tools: set an availability SLO plus RTO and RPO per service. 99.9% allows roughly 8.8 hours of downtime a year, so a ledger and a marketing page should not share one target.
- Remove single points of failure. I run stateless services behind a load balancer with health checks across at least two availability zones, and a replicated database with automated failover. A synchronous replica keeps RPO at zero but adds write latency; an asynchronous one can lose recent writes.
- Contain failures. Every remote call gets a timeout. Only idempotent operations get retries with exponential backoff and jitter. Circuit breakers stop calls to a failing dependency, and bulkheads (separate connection pools) stop one slow dependency from starving the others.
- For money movement, an idempotency key on each transfer request makes a client retry after a timeout return the original result instead of posting twice.
- Prove it works: failover drills and fault injection, alerting on SLO burn rate, and graceful degradation, such as showing a cached balance with its timestamp when the live source is down.
Follow-up
- During database failover, how do you prevent split brain, with two nodes both accepting writes?
- A zone fails while a transfer is in flight. What state is the transfer in, and how does the client find out?
- How do you stop thousands of clients from retrying at once and overloading a recovering service?
Can you explain the trade-offs between different database consistency models?
Medium · Technical and Domain Proficiency
Approach
- Lay out the spectrum. Linearizable: every read sees the latest committed write, in real-time order. Then sequential and causal consistency, then session guarantees (read-your-writes, monotonic reads), then eventual consistency, where replicas only converge once writes stop.
- State the cost. Strong consistency needs coordination (a leader with synchronous replication or a consensus quorum), so each write pays a quorum round trip, and during a partition some requests must be refused. Eventual consistency keeps latency low and stays available, but readers can see stale data, and concurrent writes need a resolution rule; last-writer-wins silently drops updates.
- Separate replica consistency from transaction isolation. In PostgreSQL the default is Read Committed. Repeatable Read is snapshot isolation and still allows write skew. Serializable uses SSI and can abort a transaction with a serialization failure that the application must retry.
- Apply it to banking. Debiting a balance needs strong guarantees: a serializable transaction or a conditional UPDATE ... WHERE balance >= amount. A transaction-history view or notification feed can be eventually consistent, with read-your-writes so users see their own action.
- For quorum stores with N replicas, R + W > N makes every read quorum overlap the most recent write quorum. Say what that buys you and that on its own it does not guarantee linearizability.
Follow-up
- Which anomaly does snapshot isolation still allow, and how would you prevent it in a schema?
- Reads go to asynchronous replicas. How do you give a user read-your-writes right after a transfer?
- What does PACELC add to CAP for a system that is rarely partitioned?
How do you approach testing in a microservices architecture?
Medium · Technical and Domain Proficiency
Approach
- Within each service: unit tests for domain rules, then component tests that run the service alone with its HTTP dependencies stubbed (WireMock, for example) and a real database in a container (Testcontainers) rather than a mocked repository.
- At service boundaries, use consumer-driven contract tests such as Pact. The provider's CI then fails when a change breaks what a consumer relies on. For events, add schema compatibility checks in a schema registry.
- Keep end-to-end tests few and focused on critical journeys like login and a transfer. They are slow and flaky and cannot be the main safety net, so most confidence has to come from the layers below.
- Test failure behaviour on purpose: downstream timeouts, retries against idempotent endpoints, and duplicate or out-of-order messages in consumers. Use fault injection in a shared environment.
- Extend testing into production with canary releases, feature flags, synthetic checks on key journeys, and trace IDs, so a failure that crosses services can be followed.
Follow-up
- How do you stop a shared staging environment from becoming the bottleneck for every team?
- A provider wants to rename a field that three consumers read. How do contract tests change that rollout?
- How do you test that a consumer handles the same event delivered twice?
Describe a time you had to optimize a slow-performing API endpoint.
Medium · Technical and Domain Proficiency
Approach
- Anchor the story in numbers: which endpoint, its traffic, and p50/p95/p99 latency before and after. Say I measured before changing anything.
- Show the diagnosis sequence. I used a trace or APM view to split time between application code, database and downstream calls. Then I ran EXPLAIN ANALYZE on the slow query, checked for ORM N+1 queries, full scans from a missing index, unbounded result sets, sequential calls that could run in parallel, and connection-pool waits.
- Name the fix and its trade-off: a composite index matching the filter and sort order (costs write throughput and storage), batching or eager loading to remove N+1, keyset pagination instead of a large OFFSET, running independent downstream calls in parallel, or a cache with an explicit TTL and invalidation rule, which risks stale financial data.
- Close with verification: a load test that reproduced the slowness, the percentile comparison after the change, and a guard against regression (a latency alert or performance test in CI).
- The answer fails if it claims a speed-up with no before/after measurement, or if the fix was found by guessing rather than from evidence.
Follow-up
- How did you confirm the bottleneck before you changed code?
- What would you have done if the slow part was a third-party API you could not change?
- How did you make sure a cache never served stale data where staleness mattered?
What strategies do you use for managing state across complex client-side applications?
Medium · Technical and Domain Proficiency
Approach
- Classify state before choosing tools: server data cached on the client, global client state (session, preferences), local component state, URL state (filters, page, selected account) and form state. Each belongs in a different place.
- Treat server data as a cache. Use a query library such as TanStack Query, SWR or RTK Query with keyed queries, stale times, invalidation after mutations, and optimistic updates that roll back when the server rejects them.
- Keep global state small and normalised by ID so one entity is never stored twice. Compute derived values with memoised selectors instead of storing them. Keep updates one-directional and immutable so changes can be traced.
- Watch performance and correctness. A single large React context re-renders every consumer on each change, so split contexts or use a store with selector subscriptions. Put shareable view state in the URL so refresh and deep links work.
- For a banking front end, keep tokens out of localStorage, because any injected script can read it; prefer httpOnly cookies. Clear sensitive state on logout and sync logout across tabs with BroadcastChannel or the storage event.
Follow-up
- The server rejects an optimistic update after the UI has already moved on. How do you reconcile it?
- Two tabs are open and the user logs out in one. What happens in the other?
- When would you not use a global store at all?
Distributed Systems – Understanding CAP theorem and consistency.
Medium · Architecture and Scalability
Approach
- State CAP precisely: when a network partition happens, a replicated system must choose between consistency (linearizable reads and writes) and availability (every non-failed node returns a non-error response). Without a partition you can have both, so 'CA' is not a real option for a distributed system.
- Give examples on each side. etcd and ZooKeeper are CP: they need a majority quorum, so the minority side refuses writes. Dynamo-style stores such as Cassandra lean AP with tunable consistency; QUORUM reads plus QUORUM writes overlap, at the cost of availability during a partition.
- Point out that the C in CAP means linearizability, not the C in ACID (constraints holding inside a transaction), and that CAP says nothing about latency. PACELC adds that even without a partition you trade latency against consistency.
- Map it to banking. Balance checks and debits should be CP: refuse or queue the request rather than risk a double spend. Profile preferences, notification feeds or analytics can be AP and reconcile later.
Follow-up
- On the minority side of a partition, what should a funds-transfer request return to the user?
- How does Raft make sure a committed entry survives the loss of the leader?
- Give a banking feature where you would knowingly choose availability over consistency.
Performance Tuning – Identifying bottlenecks and optimizing for latency.
Medium · Architecture and Scalability
Approach
- Define the target first: a percentile (p95/p99) under a stated load, not an average. Then capture a baseline under realistic traffic.
- Locate time before optimising. Use distributed traces and CPU profiles (flame graphs) for code paths, the USE method (utilisation, saturation, errors) for each resource, and the database slow-query log with EXPLAIN ANALYZE and lock-wait views. Also check GC pauses and pool saturation.
- Use Little's law (concurrency = throughput x latency) to reason about pool and thread sizing. Latency rises sharply as utilisation approaches saturation, because of queueing.
- Fix in order of leverage: remove work (N+1 queries, over-fetching), then fix indexes or rewrite queries, then batch, cache or parallelise, then make non-critical work asynchronous, and only then add hardware.
- For tail latency, remember that fan-out amplifies it: a request waiting on many backends is as slow as the slowest one. Use tight timeouts, hedged requests for idempotent reads, and fewer serial hops.
Follow-up
- How can average latency improve while p99 gets worse?
- How do you tell a CPU-bound service from an I/O-bound one in production?
- How would you size a database connection pool for a service with many instances?
Security by Design – Incorporating security into every stage of the development lifecycle.
Medium · Architecture and Scalability
Approach
- Threat-model at design time. Draw the data flow, mark trust boundaries and assets (credentials, account data, personal information), and walk STRIDE (spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege) across it.
- Build controls into the design. Authenticate with OIDC/OAuth 2.0 and MFA. Check authorisation on every object access to prevent IDOR/BOLA. Use least-privilege service identities, TLS in transit and encryption at rest with managed, rotated keys.
- In code and CI: parameterised queries, output encoding, input validation at the boundary, and no secrets in source. The pipeline runs SAST, dependency (SCA) scanning, secret scanning and IaC scanning, blocking only on high-severity findings so the gate stays trusted.
- In operation: rate limiting and lockouts against credential stuffing, audit logs that record who did what without logging secrets or full account numbers, patch cadence, DAST and penetration tests, and an incident runbook.
- Use OWASP Top 10 and ASVS as the checklist, and admit the trade-off: noisy scanners slow delivery unless someone owns triage.
Follow-up
- How would you stop a user from reading someone else's account by changing an ID in the URL?
- Where do you store secrets, and how do you rotate one without downtime?
- What goes into an audit log for a funds transfer, and what must never go in it?
Refactoring – Improving existing codebases without disrupting functionality.
Medium · Code Quality and Maintainability
Approach
- Pin the current behaviour first with characterisation tests on the code you will touch, edge cases included, so any change in output shows up as a failing test.
- Refactor in small behaviour-preserving steps (extract function, rename, introduce an interface or seam). Keep each commit green, and keep refactor commits separate from behaviour changes so reviewers can check them.
- For larger rewrites, use the strangler fig pattern. Route a slice of traffic to the new implementation behind a feature flag, or shadow-run it and compare outputs against the old path before switching.
- For schema changes, use expand/contract: add the new shape, dual-write, backfill in batches, move reads, then remove the old shape once nothing reads it.
- Justify the work with evidence such as change frequency, defect rate in that module, or lead time, and say how you would roll back at each step.
Follow-up
- How do you refactor code with no tests and no written spec?
- How would you convince a product manager to fund this instead of a feature?
- Halfway through a strangler migration the new path shows a discrepancy. What do you do?
Testing Paradigms – Unit, integration, and end-to-end testing strategies.
Medium · Code Quality and Maintainability
Approach
- Define the layers precisely. Unit tests cover one function or class with collaborators faked; they are fast and deterministic. Integration tests exercise real interactions with a database, queue or HTTP client. End-to-end tests drive the whole system through the UI or public API, as a user would.
- Explain the proportion and its trade-off. The test pyramid has many unit tests, fewer integration tests and a handful of end-to-end tests, because confidence per test rises as speed and determinism fall. The testing trophy instead weights integration tests more heavily.
- Place each concern deliberately. Business rules such as interest or fee rounding go in unit tests, with money in decimal types, never floats. SQL, migrations and serialisation go in integration tests against a real database in a container. Login and transfer journeys go in end-to-end tests.
- Control flakiness: isolated test data, no fixed sleeps (wait on conditions), an injectable clock, and quarantine plus an owner for any flaky test.
- Treat coverage as a floor, not a goal. Mutation testing shows whether the assertions would actually catch a change.
Follow-up
- When does mocking the database give false confidence?
- A flaky end-to-end test is blocking a release. What do you do today, and what do you change afterwards?
- How would you test code that depends on the current date, such as month-end statement generation?
– Providing constructive, high-quality feedback to peers.
Medium · Code Quality and Maintainability
Approach
- Frame it around code review. Label each comment as blocking (correctness, security, data loss, broken contracts) or as a suggestion or nit, so the author knows what must change.
- Be specific and explain consequences. Point at the line, say what breaks and under which input, and offer an alternative or a question instead of an instruction.
- Move style debates to linters and formatters so review time goes to logic, tests and design. Ask for small pull requests, and review the design early on large changes, before code exists.
- Tell one real story: the feedback you gave, how it was received, what changed in the code or the person's later work, and what you changed in how you give feedback.
- The story fails if it only shows you being right and the author being wrong, with no sign of how the working relationship held up.
Follow-up
- Tell me about a time someone pushed back on your review comment. Who turned out to be right?
- How do you review a change in a part of the codebase you do not know?
- How do you give critical feedback to an engineer more senior than you?
Technical Skills: Proficiency in modern programming languages and frameworks relevant to the team's stack is e
Medium · Role Requirements & Qualifications
Technical Skills: Proficiency in modern programming languages and frameworks relevant to the team's stack is essential. Experience with cloud platforms and microservices architecture is highly valued.
Approach
- Prepare a walkthrough of your main language that goes below syntax: its concurrency model (threads, an event loop, async/await), memory management and garbage collection, and one production problem you fixed because you understood them.
- For microservices, say when splitting pays off (independent deploys, clear ownership, different scaling needs) and what it costs: network failures, no cross-service ACID transactions, harder debugging. Name the patterns that address those costs: sagas with compensating actions, the transactional outbox, distributed tracing.
- For cloud work, name the managed services you have actually run (compute, managed databases, queues, object storage) and how you handled IAM least privilege, autoscaling, multi-zone deployment and cost.
- Be exact about depth. Separate what you have shipped and operated from what you have only read about, and be ready for detail on the former.
Follow-up
- When would you keep a feature inside an existing service instead of creating a new one?
- How do you keep data consistent between two services without a distributed transaction?
- Walk me through how a request is authenticated and authorised between two of your services.
Experience: A track record of delivering complex software projects in a fast-paced environment. Senior roles w
Medium · Role Requirements & Qualifications
Experience: A track record of delivering complex software projects in a fast-paced environment. Senior roles will require evidence of technical leadership and architectural decision-making.
Approach
- Pick one project and set the scope fast: the problem, the constraints, the team, and your specific role, in first person singular.
- Present one architectural decision as a trade-off: the options considered, the criteria (risk, cost, latency, team skills), why you chose as you did, and where it was recorded (an architecture decision record or design doc).
- Show how you delivered under uncertainty: a spike to remove the biggest unknown, thin vertical milestones, feature flags for incremental release, and how you handled a scope change.
- Finish with measured outcomes, and for senior roles with leadership evidence: whom you aligned, how you resolved a disagreement, whom you mentored.
- The answer is weak if it narrates the team's work without separating your own decisions, or if it has no cost or downside of the choice.
Follow-up
- What would you decide differently today, knowing what you know now?
- How did you win over the engineer who disagreed most?
- What did that decision cost the team a year later?
Soft Skills: Excellent communication skills, the ability to work in a remote-first culture, and a proactive mi
Medium · Role Requirements & Qualifications
Soft Skills: Excellent communication skills, the ability to work in a remote-first culture, and a proactive mindset toward learning and development.
Approach
- For communication, prepare an example of explaining a technical trade-off to a non-technical stakeholder: lead with the decision needed and its business impact, give two or three options with cost and risk, avoid jargon, and confirm understanding.
- For remote work, show written, asynchronous habits: design docs before building, decision logs, pull request descriptions that explain why, status updates sent before anyone asks, and use of overlap hours for discussions that need real-time exchange.
- For learning, name something you learned recently, how you learned it (built something, read the source) and where you applied it, with the result.
- Back every claim with a specific story. Self-descriptions without evidence add nothing.
Follow-up
- Tell me about a misunderstanding on a distributed team and how you resolved it.
- A project is slipping. How do you tell stakeholders, and when?
- What is the last technical thing you learned, and what did you build with it?
Structure your answers: Use the STAR method (Situation, Task, Action, Result) for behavioral questions to keep
Medium · Other General Tips
Structure your answers: Use the STAR method (Situation, Task, Action, Result) for behavioral questions to keep your responses concise and impactful.
Approach
- Keep Situation and Task to a sentence or two each: enough context to understand the stakes, then move on.
- Put the detail in Action, in first person singular: the decisions you made, the alternatives you rejected and why, and how you worked with others.
- End with a measurable Result (latency, incidents, delivery date, money) and one thing you learned or would do differently.
- Build a bank of five or six stories covering a production incident, technical debt, a disagreement, a failure, a deadline and mentoring. Rehearse each until it lands without notes.
- The two usual failures are a long setup with no clear action, and a story with no result or no personal contribution.
Follow-up
- What was the measurable outcome?
- What would you do differently if it happened again?
- How did the people who disagreed with you see the outcome?
Think out loud: During technical assessments, your interviewer is more interested in your problem-solving proc
Medium · Other General Tips
Think out loud: During technical assessments, your interviewer is more interested in your problem-solving process than just the final answer.
Approach
- Before writing code, restate the problem aloud and confirm inputs, constraints and edge cases (empty input, duplicates, very large sizes).
- State the brute-force solution and its complexity, then the improvement and why it works, naming the data structure (hash map, heap, two pointers) and the new time and space complexity.
- Narrate decisions at branch points rather than every keystroke. When stuck, say what you are checking and which hypothesis you are testing.
- Test aloud: trace one normal example by hand, then the edge cases, and fix bugs while explaining what was wrong.
- Practise by recording yourself on a practice problem and cutting long silences and vague filler.
Follow-up
- What is the time and space complexity, and can you do better?
- Which input would break this solution?
- Why did you choose this data structure over the alternatives?
Ask meaningful questions: Use the time at the end of your interviews to ask about the team's current technical
Medium · Other General Tips
Ask meaningful questions: Use the time at the end of your interviews to ask about the team's current technical challenges or the company's long-term vision.
Approach
- Prepare questions about the team's actual engineering: its current technical challenges, how changes reach production, how on-call and incident reviews work, and how architecture decisions get made and recorded.
- Ask where the role sits: the core banking platform or MANTL work, since both appear in descriptions of this role. Also ask what the first months of work would involve.
- Tie at least one question to something said earlier in the interview. This shows you listened and gives you information for your decision.
- Skip questions a public page already answers, and keep a short list ready for each interviewer so you never repeat yourself across stages.
Follow-up
- What would you want to work on first if you joined?
- Why are you interested in financial technology?
- What would make you turn down an offer here?
One customer endpoint stalls deliveries to every other destination
Medium · bulkhead · timeouts · saturation
The egress service delivers about 1.5k webhooks/second across 40,000 destinations, with a per-destination concurrency cap of 4 and a 10-second connect-plus-read timeout. Throughput falls to 300/second, queue depth climbs, and p99 delivery latency for unaffected destinations goes from 200 ms to minutes, while the error rate barely moves. One tenant holds 900 destination rows whose URLs share a hostname that now answers in 9.5 seconds. Explain the mechanism with the arithmetic, then give the containment in the order you would apply it.
Approach
- Look at saturation before errors. A flat error rate with collapsing throughput says nothing is failing, things are waiting, so the first signal to pull is in-flight request count or pool wait time rather than the error counter. This is the distinction that decides the whole investigation.
- Group in-flight work by resolved host, not by destination id. The cap is keyed per destination row, so 900 rows sharing one hostname buy 3,600 concurrent slots against a single host, each held for 9.5 seconds. The bulkhead was never a bulkhead for that host, and grouping by the wrong dimension is why the dashboard looked healthy.
- Do the arithmetic in both directions. Required concurrency is arrival rate times latency, so 1.5k/second at 200 ms needs about 300 in flight, which is entirely consumed by 3,600 slow slots; conversely whatever concurrency is left sustains rate equals concurrency divided by 9.5 seconds, which is the 300/second you are seeing. Matching both numbers is what promotes this from a plausible story to the mechanism.
- Explain why the circuit breaker never helped. It opens on consecutive failures, and a 9.5-second response inside a 10-second timeout is a success. Slow is not failing, so an error-rate breaker cannot see this; you need a slow-call ratio, a deadline propagated from the caller's remaining budget, or a concurrency limiter.
Follow-up
- The host recovers to 80 ms. How long does the queue take to drain, and what does the drain do to the recovered host?
- Where should the 10-second timeout number actually come from?
Suggested practice checklist
The plan follows the reported topics in order of how much explanation each needs: format first, then availability and consistency, performance, testing and refactoring, security and client-side state, behavioral stories, and a final spoken run-through.
Day 1 — Confirm the format and map the reported topics
- Email your recruiter asking for the stage list, who you will meet, whether you will write code live, and whether a system design discussion is included.
- Sort the reported questions into four groups: distributed systems, performance and security, testing and refactoring, and behavioral. Mark the two you can answer worst.
Deliverable: A sent recruiter email with format questions, and a one-page map of reported topics with your two weakest marked.
Day 2 — Availability, fault tolerance and consistency
- Sketch a funds-transfer service and list its failure modes: instance loss, zone loss, a dependency timing out, a database failover. Next to each, write the mitigation, such as redundancy, health checks, timeouts, retries with backoff and jitter, circuit breakers and idempotency keys.
- Write a short comparison of strong, causal, read-your-writes and eventual consistency, each with one banking example and the cost it adds.
- Explain CAP aloud in two minutes using the transfer example and an activity feed, covering what each does during a network partition.
Deliverable: A failure-mode table for the transfer service, a consistency comparison with examples, and a recorded two-minute CAP explanation.
Day 3 — Performance tuning and latency
- Write your slow-endpoint story in order: symptom with a percentile, how you measured, the bottleneck, the change, the before and after numbers.
- List five common causes of a slow API (N+1 queries, missing or unusable index, chatty downstream calls, large payloads, no caching) and how you would confirm each one.
- Work the SQL practice question on why an owner filter ignores a listing index, then the debugging practice question on one customer endpoint stalling deliveries to every other destination.
Deliverable: A written slow-endpoint story with measurements, a five-cause diagnosis checklist, and notes from both practice questions.
Day 4 — Testing strategy and refactoring
- Write a test plan for a three-service request chain: unit tests per service, consumer-driven contract tests at each boundary, dependency doubles, and a short list of end-to-end paths.
- Outline how you would refactor a large module without changing behavior: add characterization tests first, make small reversible steps, keep the old path behind a flag until the new one is proven.
- Work the practice coding question on archiving a resource graph without breaking live references, and note which tests you would write before touching the code.
Deliverable: A microservice test plan, a refactoring outline with its safety net, and a written test list for the resource-graph exercise.
Day 5 — Security by design and client-side state
- Write a security checklist per lifecycle stage: threat model at design, authentication and authorization, input validation, secrets handling, dependency scanning in CI, audit logging, review checklist items.
- Pick the front-end framework you know best and write a one-page answer on managing state: local component state, shared stores, server-state caching, derived state, and when each becomes a problem.
- Explain both answers aloud once, and cut anything you cannot justify in one sentence.
Deliverable: A lifecycle security checklist, a one-page client-state answer, and one recorded spoken run of each.
Day 6 — Behavioral stories: incidents, debt and feedback
- Write the incident story and the technical-debt story in situation, task, action, result form, each stating your own decision, the alternative you rejected and a measurable result.
- Write a story about giving constructive code-review feedback, including what you said, how it was received and what changed.
Deliverable: Three written stories (incident, technical debt, feedback) in STAR form, plus notes from the behavioral practice questions.
Day 7 — Spoken run-through and questions to ask
- Give one full answer aloud to a design-style prompt (high availability for a banking service), narrating your reasoning as you go, then record and review it.
- Write four questions to ask the team about its current technical challenges, how it handles incidents and technical debt, and how remote collaboration works.
- Do a timed pass over the five direct reported questions with no notes, and mark any answer that lacked a mechanism, a trade-off or a failure case.
Deliverable: A recorded design answer, four written questions for the interviewers, and a list of answers that still need work.
Behavioral prompts for Alkami Technology
Candidates report behavioral assessments alongside the technical deep-dives, and one reported tip is to bring specific examples of production incidents and technical debt. Reports also describe ambiguous problems, work with product managers and DevOps, remote collaboration and peer code review, so keep a story ready for each. Use situation, task, action, result, and make your own decision and its measurable outcome the center of every story.
- Describe a time you had to optimize a slow-performing API endpoint.
- Walk through a production incident you handled: how you detected it, what you did first, how you communicated, what the root cause was and what you changed afterwards.
- Describe technical debt you inherited or created. What did you decide to fix or leave, how did you justify it, and what was the result?
- Tell me about a time you gave a peer constructive feedback in a code review that they initially disagreed with.
- Describe a technical problem with no clear right answer. How did you research it, prototype options and present a recommendation to your team?
- Explain a complex technical trade-off you once had to describe to a non-technical stakeholder, and how you checked they understood it.
Tell callers you do not own that their integration breaks
Medium · deprecation · compatibility · stakeholders
A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.
Approach
- Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
- Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
- Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
- Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.
Follow-up
- How would you detect a consumer that reads the field only during a monthly export?
- One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?
Ship under a deadline and bound the debt you chose
Medium · pagination · technical debt · tradeoffs
You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.
Approach
- Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
- Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
- Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
- Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.
Follow-up
- At what page depth does the offset version breach your latency budget, given your page size and row counts?
- What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?
Estimate work you have never done and defend the range
Hard · estimation · backfills · expand-contract
You are asked to estimate a change you have never attempted: add a column to a 100-million-row table, populate it, move reads across, and drop the old shape. Give a range with the assumptions that generate it, including batch size, the signal your backfill throttles on, and wall-clock hours, and name the three unknowns that would move the number most. Then describe a real estimate you gave under comparable ignorance: how you expressed its uncertainty, what you committed to, and how wrong you turned out to be.
Approach
- Decompose into independently deployable steps before estimating anything: add the column nullable, write both shapes, backfill in batches, verify, move reads, stop writing the old shape, drop it. That is four deploys spread over days, and the calendar estimate is dominated by them rather than by the loop's runtime.
- Do the arithmetic aloud for the part that has arithmetic in it: batch size times number of batches times per-batch duration, at a write rate the primary can absorb alongside roughly 1.2k writes per second of production traffic. The loop is throttled by replication lag and lock waits, not by how fast it can issue statements.
- Price the schema step by its lock rather than its statement duration. In PostgreSQL an ALTER TABLE taking ACCESS EXCLUSIVE waits for every open transaction on that table while later queries queue behind it, so a millisecond change issued during a thirty-second analytics query stalls that table for thirty seconds. Adding a nullable column with a non-volatile default avoids a rewrite from version 11; a new index wants CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block and leaves an invalid index behind if it fails.
- Express the answer as a range whose endpoints each trace to a stated assumption, then name the cheapest experiment that collapses it, which is almost always running one real batch against the real table and multiplying.
Follow-up
- How do you verify the backfill genuinely finished, given rows written by production traffic while it ran?
- Where does the backfill resume from after a worker is killed mid-batch, and what makes that resume point trustworthy?