programming3 min read

Tutorial: Learn Crystal from Scratch (2026)

Tutorial: Learn Crystal from Scratch (2026)

Published:  |  Category: Programming  |  Reading time: ~15 min
Tutorial: Learn Crystal from Scratch (2026)

Crystal combines Ruby-like syntax with compiled performance via LLVM. I have used it for web APIs, CLI tools, and high-throughput data pipelines. The syntax is so close to Ruby that many Ruby scripts need only minor adjustments to compile with Crystal.

Crystal's concurrency model — fibers with channels, inspired by Go and Erlang — makes it easy to write efficient concurrent programs. The type inference is global, so you rarely need type annotations.

Type Inference

Crystal's type inference is global and powerful. The compiler analyzes your entire program to determine types without annotations. Union types are inferred automatically when a variable can hold multiple types. The compiler reports type errors with detailed messages about where incompatible types originate.

def process(x)
  if x.is_a?(Int32)
    x + 1
  elsif x.is_a?(String)
    x.upcase
  else
    "unknown"
  end
end

puts process(42)     # Int32
puts process("hi")   # String
puts process(true)  # String

# Inferred return type: (Int32 | String)

Fibers and Channels

Fibers are lightweight coroutines managed by Crystal's runtime. Channels provide CSP-style communication: send puts a value, receive gets it. Multiple fibers can operate on the same channel. The runtime schedules fibers on a thread pool. Use spawn to create a fiber and Channel(T) for typed channels.

channel = Channel(Int32).new

spawn do
  10.times do |i|
    channel.send(i * 10)
  end
  channel.close
end

spawn do
  while val = channel.receive?
    puts "Got: #{val}"
  end
end

sleep 0.1

Macros

Crystal macros run at compile time, operating on AST nodes. They enable metaprogramming similar to Nim but with Ruby-like syntax. macro definitions create compile-time code generators. {{ }} interpolates into the generated code. Macros can inspect types, iterate over fields, and generate methods and types.

macro define_getters
  {% for type in @type.instance_vars %}
    def {{type.name}}
      @{{type.name}}
    end
  {% end %}
end

class Person
  @name : String
  @age : Int32

  define_getters

  def initialize(@name, @age)
  end
end

p = Person.new("Alice", 30)
puts p.name
puts p.age

Shards

Shards is Crystal's package manager. Dependencies go in shard.yml. shards install resolves and installs them. Shards supports git and path sources. Crystal's standard library is extensive — HTTP servers, JSON/XML parsers, crypto, and more are built in, reducing the need for third-party packages.

# shard.yml
name: myapp
version: 0.1.0

dependencies:
  kemal:
    github: kemalcr/kemal
    version: ~> 1.0
  pg:
    github: will/crystal-pg
    version: ~> 0.25

# Install: shards install

HTTP and Web

Crystal's standard library includes HTTP::Server for building web applications without frameworks. The server uses fibers per request, enabling high concurrency. The Kemal framework provides Sinatra-like syntax. Crystal's HTTP client supports streaming, SSL, and connection pooling.

require "http/server"

server = HTTP::Server.new do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello, World!"
end

address = server.bind_tcp 8080
puts "Listening on #{address}"
server.listen

# With Kemal framework:
# require "kemal"
# get "/" do
#   "Hello, World!"
# end
# Kemal.run

C Bindings

Crystal calls C libraries via @[Link] and fun declarations. The @[Link] attribute tells the linker which library to use. Crystal handles pointer types, callbacks, and struct mapping. There are bindings for libcurl, sqlite3, openssl, and many popular C libraries.

@[Link("sqlite3")]
lib SQLite3
  fun open(path : UInt8*, db : Void**) : Int32
  fun close(db : Void*) : Int32
  fun exec(
    db : Void*,
    sql : UInt8*,
    callback : (Void*, Int32, Void**, Void**) -> Int32,
    arg : Void*,
    errmsg : Void**
  ) : Int32
end

db = Pointer(Void).malloc(1)
SQLite3.open("test.db", pointerof(db))
puts "DB opened"

Frequently Asked Questions

Crystal vs Ruby?

Crystal compiles to native code, is statically typed, and performs 10-100x faster for CPU-bound work. Syntax is almost identical to Ruby.

Does Crystal have a garbage collector?

Yes, Crystal uses an incremental, generational GC based on Boehm-Demers-Weiser. There are no manual memory management calls.

What are Crystal shards?

Shards are Crystal packages, similar to Ruby gems or npm packages. They are distributed via Git repositories.

Can I use Crystal on Windows?

Crystal has experimental Windows support via MinGW. Linux and macOS are the primary targets.

Originally published on Ayodhyyya. Last updated June 1, 2026.