Skip to main content

Stripe

Elixir engineer at Stripe

Stripe is known for real-world, rigorous coding rather than abstract puzzles. Onsite rounds include practical implementation work and a bug-bash style round where you fix and extend a small codebase. The written-communication bar is high across every role, and system design rounds expect production realism.

Process timeline

  1. 1

    Recruiter screen

    Background and role fit.

  2. 2

    Practical coding

    Implementing real features rather than solving riddles.

  3. 3

    Bug squash

    Debugging and extending an existing codebase under time.

  4. 4

    System design

    Production-grade design with real failure handling.

  5. 5

    Behavioral

    Collaboration, judgment, and written-communication signal.

What Stripe looks for

What they value

  • Writing clean, working code in a realistic setting
  • Fast, careful debugging of unfamiliar code
  • Clear writing and reasoning under pressure

Culture signals

  • Rigor and getting the details exactly right
  • Strong written communication as a core skill
  • Caring about developers and end users of the API

Interview questions

Elixir engineer questions worth preparing alongside the Stripe rounds described above, drawn from our elixir engineer question bank.

As asked

Design the OTP supervision tree for a Phoenix service that handles chat rooms, user presence, and outbound webhooks. What processes exist and what restarts when something fails?

Sample answer outline

A strong answer starts with process boundaries, not modules. Put room processes under a DynamicSupervisor so rooms can start and stop independently, and isolate webhook delivery workers so a bad third-party endpoint cannot take down chat state. Choose restart strategies deliberately: one_for_one for independent room workers, rest_for_one only where later children depend on earlier ones. Presence should be treated as derived state that can be rebuilt, while durable chat messages belong in the database. Candidates often trip up by making one GenServer own too much state or by restarting a whole subtree for a local failure.

Expect these follow-ups

  • When would you use Registry versus pg for finding room processes?
  • How do you avoid a thundering restart loop after a deploy?
  • What state is safe to keep only in a process?
otpsupervisionphoenix

As asked

When would you choose handle_call over handle_cast in a GenServer, and what are the failure modes you need to think about for each?

Sample answer outline

handle_call is synchronous, gives a reply, and lets the caller know if the server crashed (it gets an error). handle_cast is fire-and-forget, faster, but the caller has no visibility into failure or ordering. A strong answer mentions that using cast for state mutation without knowing if it succeeded can cause subtle bugs, and that call adds back-pressure naturally while cast can overwhelm a slow server.

Expect these follow-ups

  • How would you implement a timeout on a GenServer call, and what happens to the server when the caller times out?
  • Could you ever replace a synchronous call with a cast plus a separate monitor on the server? What would that buy you?
genserverotpconcurrencyback-pressure

As asked

Describe the difference between the :one_for_one, :one_for_all, and :rest_for_one supervisor strategies. Give me a concrete scenario where you would choose each one. How does DynamicSupervisor replace the old :simple_one_for_one strategy, and when would you reach for it?

Sample answer outline

A strong answer gives concrete examples: :one_for_one for independent workers like connection pool members, :one_for_all when child processes share state and a crash in one invalidates the others (e.g., a registry and its worker), :rest_for_one when children have a pipeline dependency where later children depend on earlier ones. For dynamically spawned homogeneous workers like per-user sessions, the answer should name DynamicSupervisor, which replaced :simple_one_for_one in Elixir 1.6 and OTP 20. DynamicSupervisor.start_child/2 is the idiomatic way to add children at runtime.

Expect these follow-ups

  • What are max_restarts and max_seconds, and what happens when a supervisor hits those limits?
  • How would you design a supervision tree for a Phoenix channel that manages per-user state?
otpsupervisorfault-toleranceprocesses

As asked

Explain how Phoenix LiveView decides what HTML to send over the WebSocket on each update. What are assigns, what is the diff algorithm doing, and how can poorly structured templates hurt performance?

Sample answer outline

LiveView tracks assigns in the socket, renders templates through a compile-time analysis that separates static and dynamic parts, and on each change only sends the dynamic segments that changed. A strong answer mentions that deeply nested or computed values embedded directly in templates bypass this optimization, and that using components and passing assigns explicitly allows finer-grained diffing.

Expect these follow-ups

  • How does the phx-update attribute on the client side affect how LiveView applies DOM patches?
  • When would you use a live component versus a function component in LiveView?
phoenixliveviewwebsocketperformancefrontend

As asked

You are reviewing a Phoenix endpoint that lists 100 blog posts and their authors. Someone wrote it with Repo.all(Post) followed by Repo.get(User, post.user_id) in a loop. How do you fix this, and what Ecto features make it idiomatic?

Sample answer outline

The fix is to use Ecto preloads: either preload in the original query with a join, or use Repo.preload/2 which batches the secondary query into a single IN clause. A strong answer distinguishes between query preloads (join-based, single query) and Repo.preload (separate query with IN), and knows when each is appropriate. Mentioning dataloader for GraphQL contexts is a bonus.

Expect these follow-ups

  • How would you detect N+1 queries automatically in your test suite or development environment?
  • When would a join-based preload be worse than a separate batch query?
ectodatabasesperformancepostgresqlqueries

As asked

How do you structure changeset pipelines in Ecto when you have different validation rules for create versus update, and how do you handle embedded schemas with their own changesets?

Sample answer outline

A strong answer shows using separate named functions (e.g., create_changeset/2 and update_changeset/2) that call a shared base changeset, using cast_embed/3 or cast_assoc/3 for nested schemas, and using with_action/2 to distinguish insert from update constraints. Knowing the difference between validate_ functions (pure) and constraint/3 functions (database-backed) is essential.

Expect these follow-ups

  • What is the difference between validate_required/3 and a NOT NULL database constraint from Ecto's perspective?
  • How do you unit test a changeset without hitting the database?
ectochangesetsvalidationschemasdatabases

Elixir engineer interview detail at Stripe

How the Stripe loop applies to Elixir engineer candidates

Stripe is a late-stage unicorn headquartered in South San Francisco, and the same 5-stage process described above is what a elixir engineer candidate walks through, with the technical stages tuned to the engineering discipline. Stripe is known for real-world, rigorous coding rather than abstract puzzles. Onsite rounds include practical implementation work and a bug-bash style round where you fix and extend a small codebase. The written-communication bar is high across every role, and system design rounds expect production realism.

For a elixir engineer, the load concentrates on practical coding and system design. Those are the stages where the engineering signal is read most closely, so they are where preparation pays off most. The non-technical stages (recruiter screen, bug squash, and behavioral) still gate the offer, but they assess fit and communication rather than role-specific depth.

What the elixir engineer question mix signals

The 6 most-reported elixir engineer questions cluster around role-specific (5), backend (1). That distribution is the clearest read on what Stripe actually probes for this role: the more a topic recurs, the more reliably it shows up in the loop, so it is worth weighting practice the same way.

The set spans a easy-to-medium difficulty range, topping out at medium problems. Because the topics are concentrated rather than scattered, depth in the leading area matters more than breadth for this particular role.

What moves a elixir engineer offer forward at Stripe

Across the loop, the traits that consistently move a Stripe elixir engineer offer forward are writing clean, working code in a realistic setting, fast, careful debugging of unfamiliar code, and clear writing and reasoning under pressure. These are not abstract values; interviewers score against them, so a elixir engineer who demonstrates them explicitly - naming the tradeoff, stating the assumption, checking the edge case out loud - reads stronger than one who only reaches the right answer silently.

The behavioral and culture stages are checking for rigor and getting the details exactly right, strong written communication as a core skill, and caring about developers and end users of the api. For a elixir engineer, the most credible way to show these is through specific, recent examples from real engineering work rather than rehearsed generalities.

Salary band

Use the salary page linked below for elixir engineer bands by city, modeled from BLS, ONS, and Levels.fyi reference medians.

View salary detail

Similar companies