Your First Elixir Program
There are two ways to run Elixir code, and knowing when to use each is part of learning the language's workflow. A standalone .exs script is compiled in memory and executed immediately with the elixir command, ideal for quick experiments. A Mix project, created with mix new, organizes real application code into a compiled, testable structure under lib/ and test/. In this walkthrough you'll build the same small greeting module both ways, first as a throwaway script, then as a proper module inside a Mix project with an automated test.
Cricket analogy: Writing your first Elixir program is like a young cricketer's first outing, you can either play a quick backyard knock, a standalone script, just to feel the bat, or turn up for a proper nets session with the full academy setup, a Mix project, before playing a real match.
Writing a Standalone Script
Create a file named hello.exs containing a module definition and a call to it, then run it with elixir hello.exs from the terminal; Elixir compiles the module in memory, executes the script top to bottom, and exits, leaving no compiled artifact on disk. Note the file extension: .exs signals a script meant to be run directly, while .ex signals a source file meant to be compiled as part of a project. IO.puts/1 is used here to print output to the terminal, but it always returns the atom :ok, not the text it printed, a detail that matters once you start writing functions meant to be tested.
Cricket analogy: Running elixir hello.exs is like a quick throwdown session in the nets, the bowling machine processes your delivery immediately and you see the result on the spot, with nothing formally entered into the club's official records.
# hello.exs
defmodule Greeter do
def greet(name) when is_binary(name) do
"Hello, #{name}! Welcome to Elixir."
end
end
IO.puts(Greeter.greet("Ada"))$ elixir hello.exs
Hello, Ada! Welcome to Elixir.Building It as a Mix Module
Inside a Mix project generated with mix new hello_world, the same logic belongs in lib/hello_world.ex as a proper module, and this time greet/1 should return the greeting string rather than print it directly, keeping the function easy to test and reuse. Running iex -S mix from the project root compiles the whole project and drops you into an interactive shell with HelloWorld already loaded, so you can call HelloWorld.greet("Ada") immediately without manually requiring any files, the way you would have to in a plain script.
Cricket analogy: Adding your function to lib/hello_world.ex is like getting your name permanently entered into a franchise's official squad list, so every future training session, iex -S mix, has you already on the roster and ready to be called upon.
# lib/hello_world.ex
defmodule HelloWorld do
@moduledoc "A tiny greeting module."
def greet(name) when is_binary(name) do
"Hello, #{name}! Welcome to Elixir."
end
end$ iex -S mix
iex(1)> HelloWorld.greet("Ada")
"Hello, Ada! Welcome to Elixir.".exs files are 'Elixir Script' files, compiled in memory and run immediately, ideal for quick experiments and one-off Mix task scripts. .ex files are compiled to .beam bytecode and cached as part of a project's real modules, such as lib/hello_world.ex, which is why only .ex files are picked up automatically when you run iex -S mix.
Writing Your First Test
ExUnit, Elixir's built-in testing framework, expects test files under test/ named with a _test.exs suffix; mix new already generated test/hello_world_test.exs alongside a test/test_helper.exs that calls ExUnit.start(). Inside the test module, use ExUnit.Case brings in the test/2 macro for defining individual test cases and the assert/1 macro for checking that an expression evaluates truthily, or that both sides of an == comparison are equal. Running mix test from the project root compiles the project and executes every test file, printing a dot for each passing test and a summary of how many tests ran and how many failed.
Cricket analogy: Writing an ExUnit test with assert is like a coach setting a specific fitness benchmark, say a 12-second sprint, that a player must hit, mix test is match day where every player's benchmark is checked automatically and a pass or fail report is issued.
# test/hello_world_test.exs
defmodule HelloWorldTest do
use ExUnit.Case
test "greet/1 returns a welcome message" do
assert HelloWorld.greet("Ada") == "Hello, Ada! Welcome to Elixir."
end
end$ mix test
.
Finished in 0.03 seconds
1 test, 0 failuresIO.puts/1 prints to standard output but always returns :ok, don't confuse a function whose job is printing with one whose job is returning a usable value. If you need the greeting string itself, to test it or send it elsewhere, write a function that returns the string, and let the caller decide whether to print it.
- Elixir code can run as a standalone .exs script via elixir script.exs, with no compilation artifact left behind.
- .ex files are meant for real project modules and are compiled to .beam bytecode.
- defmodule defines a module; functions inside it are defined with def, public, or defp, private.
- iex -S mix starts an interactive shell with your Mix project's modules already compiled and loaded.
- ExUnit is Elixir's built-in testing framework; test files live in test/ and end in _test.exs.
- assert is the primary macro used inside ExUnit tests to check that an expression evaluates as expected.
- mix test compiles the project and runs the full ExUnit test suite, reporting pass and fail counts.
Practice what you learned
1. Which command runs a standalone Elixir script file directly?
2. What is the key difference between a .ex file and a .exs file?
3. What does iex -S mix do?
4. Which macro is used inside an ExUnit test to check an expected condition?
5. What does IO.puts/1 return?
Was this page helpful?
You May Also Like
Installing Elixir and Mix
A practical guide to installing Elixir on your system and getting started with Mix, Elixir's built-in build tool and project manager.
What Is Elixir?
An introduction to Elixir, a functional, concurrent programming language built on the Erlang VM for building scalable and fault-tolerant applications.
Elixir Data Types
A tour of Elixir's built-in data types, integers, floats, atoms, booleans, strings, lists, tuples, and maps, and how immutability shapes the way they behave.
Pattern Matching in Elixir
How Elixir's = operator performs structural pattern matching rather than simple assignment, and how that idea powers destructuring, function clauses, and control flow.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics