Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Elixir & Erlang/OTP Module 4

Functions and modules interview questions

Interview questions on functions and modules: short warm-ups, then what each harder question tests, the first step of a good answer and its follow-ups.

18 questions: 6 warm-ups and 12 with model answers · Levels: Fresher to senior

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher

    What is a closure in Elixir, and what does it capture?

    Answered in the lesson Anonymous functions, closures and capture

  2. Fresher

    What is the difference between def and defp, and why are Elixir functions written as name/arity?

    Answered in the lesson Modules, named functions and default arguments

  3. Fresher

    Why write several function clauses with guards instead of one function with a case or an if inside?

    Answered in the lesson Guards and multi-clause functions

  4. Fresher

    What does the pipe operator do in Elixir, and when do you use then/2?

    Answered in the lesson Pipes, dbg/2 and debugging in IEx

  5. Fresher

    What is the difference between alias, require, import and use in Elixir?

    Answered in the lesson alias, require, import and use

  6. Fresher

    What are module attributes used for in Elixir?

    Answered in the lesson Module attributes, docs and doctests

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Mid to senior

    A report starts one Task.async/1 for each of 2,000 orders with fn -> score(order, prices) end, where prices is a list of 10,000 integers loaded once. Memory climbs by hundreds of megabytes on every run. Explain what each task receives, estimate the cost from the lesson's measurements, and redesign the code.

    What it tests
    Knowing that a closure carries its captured values into every process that runs it, estimating the cost of the copies, and choosing what to capture and where shared data should live.
    First step
    Find what the closure captures: it uses order and prices, so every task gets its own copy of the whole list. The lesson measured a process running a closure over that list at 230,280 bytes, against 2,624 for one over a single number, so 2,000 tasks alive at once hold about 460 MB of copies.
    Follow-ups

    From the lesson Anonymous functions, closures and capture

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  2. Fresher to mid

    A teammate adds up an order list with total = 0 and Enum.each(orders, fn o -> total = total + o.amount end), and the report always prints 0. They suggest keeping the total somewhere global instead. Explain what happens, what the compiler already said, and how you would write it.

    What it tests
    Knowing that a name bound inside a function stays inside it, reading the compiler's unused-variable warning, and replacing changing state with Enum.reduce/3 or Enum.sum/1.
    First step
    Read the warning the compiler printed for that line: the total inside the function is unused, and there is a variable with the same name in the context. Each call binds its own new total, which is gone when the call ends; the outer total is never changed, so it stays 0.
    Follow-ups

    From the lesson Anonymous functions, closures and capture

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Fresher to mid

    Report has def build, do: build(:text). A teammate adds def build(format \\ :html) below it so callers can pick a format, and Report.build() keeps returning {:report, :text}, with only a compiler warning. Explain the warning, what happens if the two definitions swap places, and how you would design build.

    What it tests
    Knowing that a default adds a lower arity, that two definitions of one name and arity collide, and choosing between a default and an explicit function without breaking callers.
    First step
    List the arities: the definition with a default adds build/0, and the module already has an explicit build/0 above it. Elixir 1.20.4 warns that this clause for build/0 cannot match because a previous clause always matches: the explicit one runs, and the default :html is never used.
    Follow-ups

    From the lesson Modules, named functions and default arguments

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  4. Mid to senior

    Key handling lives in Payments.Internal.Keys, nested inside defmodule Payments, and the team assumes only Payments can call it. A new reporting module calls Payments.Internal.Keys.decrypt/1 directly. Was the assumption right? How do you find such callers and keep the module internal?

    What it tests
    Knowing that nested names give no access, that defp is the only rule about who may call a function, and keeping internal modules internal with a narrow public module and a check in the build.
    First step
    Check what nesting gives: Payments.Internal.Keys is a separate module whose name starts with Payments, and its public functions can be called from anywhere, as the lesson's Shop.Cart.Item was. Only defp restricts calls, and only to the one module that defines the function.
    Follow-ups

    From the lesson Modules, named functions and default arguments

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  5. Fresher to mid

    Shipping had def fee(amount) when is_integer(amount) and amount >= 0, do: 4_000. A teammate added a clause below it, when is_integer(amount) and amount >= 50_000, returning 0. Orders of 60,000 paise still pay 4,000, and the build printed no warning. Explain both, fix it, and name the tests that prove it.

    What it tests
    Ordering clauses from specific to general, knowing which overlaps the Elixir 1.20 checker reports and which it cannot, and testing every band at its edges.
    First step
    Trace fee(60_000): the first clause is tried first, 60,000 is an integer and at least 0, so it returns 4,000 and the new clause never runs. Then ask why no warning came: the first guard compares, so the checker, which reasons about types, cannot tell that the clause hides the one below.
    Follow-ups

    From the lesson Guards and multi-clause functions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  6. Mid to senior

    A teammate writes def register(%{email: email} = user) when valid_email?(email), and Elixir refuses it. They then try a defguard built on String.contains?/2 and are refused again. Explain why both fail, and design register/1 so that each kind of bad input gets a clear result.

    What it tests
    Knowing what guards may hold and why, what defguard can and cannot do, and moving validation into ordinary code whose results the clauses then match.
    First step
    Read the two errors: a local function cannot be invoked inside a guard, and a defguard body is checked like a guard, so String.contains?/2 is refused as a remote function. Guards hold only built-ins without side effects, so the check has to run as ordinary code before any clause chooses.
    Follow-ups

    From the lesson Guards and multi-clause functions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  7. Fresher to mid

    To find a failing test, a teammate added dbg/2 to a pipeline. The CI log now shows lines like ^[[36m^[[3m[lib/report.ex:12: Report.build/1]^[[0m, and setting NO_COLOR=1 in CI changed nothing. Explain the codes, how to get readable output, and how to keep such calls out of the main branch.

    What it tests
    Knowing what dbg/2 prints and that it writes colour codes even into a pipe or a log, turning them off with syntax_colors, and keeping debugging calls out of committed code.
    First step
    Read the line as the lesson's cat -v did: ^[ is the escape character and [36m a colour code. dbg/2 writes those codes whatever receives its output, a terminal, a pipe or a log file, and NO_COLOR=1 did not stop them; its own option, syntax_colors: [], does.
    Follow-ups

    From the lesson Pipes, dbg/2 and debugging in IEx

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  8. Mid to senior

    A function misbehaves only on one customer's data. You run the service with iex -S mix and want to stop the function on that customer's request and look at its values. Explain how you do it, what IEx asks first, and what can go wrong while the request is paused.

    What it tests
    Using break! on code that runs in another process, knowing that IEx asks before it pries, narrowing a breakpoint with a pattern, and the effects of a paused process on its callers.
    First step
    Set the breakpoint from the prompt, so no code changes: break! with the function and, to stop only for that customer, a pattern on its argument. The request runs in its own process, not the shell's, so IEx asks first whether to allow the pry; then read the bindings, step with next, finish with continue.
    Follow-ups

    From the lesson Pipes, dbg/2 and debugging in IEx

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  9. Mid to senior

    A module defines and calls its own then/2. After an Elixir upgrade it stops compiling with "imported Kernel.then/2 conflicts with local function". Explain why an unchanged module broke, give a fix that works on both versions, and say how a team keeps upgrades from breaking like this.

    What it tests
    Knowing that every module imports Kernel automatically, that new Kernel functions are an expected change between releases, and the backwards-compatible fix with import Kernel, except:.
    First step
    Every module imports Kernel automatically, and the newer Elixir added then/2 to Kernel. A local then/2 that the module calls now has the same name and arity as an import, so the compiler cannot tell which one the call means and refuses it, as the lesson's Stock module was refused over max/2.
    Follow-ups

    From the lesson alias, require, import and use

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Mid to senior

    After use SomeLib was added to a module, the module's own format/1 stopped compiling with "imported SomeLib.Helpers.format/1 conflicts with local function", although nothing in the module imports SomeLib.Helpers. Explain how, how you would see what use brought in, and what you would change in the module and in the library.

    What it tests
    Knowing that use runs the library's __using__/1, which can write imports into your module, inspecting a use line, and the anti-patterns guide's advice on use instead of import.
    First step
    Expand the line: use SomeLib becomes require SomeLib and a call to SomeLib.__using__/1, and the code that call returns goes into the module. If it holds import SomeLib.Helpers, every function of Helpers is now imported, format/1 included, and it clashes with the module's own format/1.
    Follow-ups

    From the lesson alias, require, import and use

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    A payments module has @base_url System.get_env("PAYMENT_URL", "https://sandbox.payments.example"). The release is built in CI, where PAYMENT_URL is not set, and in production every charge goes to the sandbox although the servers set PAYMENT_URL to the live address. Explain, fix it, and stop it from happening again.

    What it tests
    Knowing that module attributes are evaluated at compile time, separating build-time from run-time configuration, and checks that catch configuration frozen into a build.
    First step
    Find when the line runs: an attribute is evaluated while the module compiles, so System.get_env/2 ran in CI, found no PAYMENT_URL and wrote the sandbox default into the compiled module. The servers' variable is never read, as the lesson's compiled_url/0 ignored the live address.
    Follow-ups

    From the lesson Module attributes, docs and doctests

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Fresher to mid

    A team writes all its examples as doctests. The one for Report.save/1 writes a file into the project folder, another expects #PID<0.150.0>, and CI fails now and then while files pile up. Explain what goes wrong, and decide which examples stay doctests.

    What it tests
    Knowing what doctests are for and what they are not, from the ExUnit documentation, and splitting examples between doctests and ordinary tests.
    First step
    Read the ExUnit documentation's advice: doctests are not recommended when examples contain side effects. They do not run in a sandbox, so a written file stays behind, and a result such as a pid is compared as text and changes from run to run, so it cannot match reliably.
    Follow-ups

    From the lesson Module attributes, docs and doctests

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All Elixir & Erlang/OTP interview questions · The Elixir & Erlang/OTP track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.