Question
In a Ruby file such as this generated rspec binstub, what is the purpose of the magic comment # frozen_string_literal: true?
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
load Gem.bin_path("rspec-core", "rspec")
What is this comment intended to do, and how does it affect string literals in Ruby?
Short Answer
By the end of this page, you will understand what # frozen_string_literal: true means in Ruby, how it changes the behavior of string literals, why generated files often include it, and what kinds of errors or benefits it can introduce in everyday code.
Concept
In Ruby, strings are normally mutable by default. That means you can change their contents after they are created.
For example:
name = "Ruby"
name << " on Rails"
# => "Ruby on Rails"
The magic comment:
# frozen_string_literal: true
changes that behavior for string literals in that file.
With it enabled, string literals like:
"hello"
"rspec"
are automatically frozen, which means they cannot be modified.
If you try to change one, Ruby raises an error:
message = "hello"
message << " world"
# FrozenError
Why this matters
This feature helps with two common goals:
- Safety: prevents accidental modification of string literals
- Performance: Ruby can reuse immutable string objects more efficiently in some cases
Important detail
This comment affects string literals written directly in the file. It does not freeze every string object in your entire program.
For example, these are different ideas:
"hello"in a file with frozen string literals enabled → frozenString.new("hello")→ new mutable string unless you freeze it yourself- a string returned from a method → depends on how that string was created
Scope of the comment
The comment applies to the current Ruby file only. It does not automatically affect other files unless they also include the same comment.
That is why generated files like Bundler binstubs often include it explicitly: the file is declaring how string literals should behave inside that file.
Mental Model
Think of a string literal as a printed label.
Without # frozen_string_literal: true, Ruby gives you a label written in pencil, so you can erase or add to it.
With # frozen_string_literal: true, Ruby gives you a laminated label. You can read it and pass it around, but you cannot write on it.
So this:
"hello"
is no longer a changeable piece of text in that file. It becomes a read-only value.
That is useful when a string is meant to be a fixed constant like a file path, a key, a command name, or a message template.
Syntax and Examples
The magic comment goes near the top of a Ruby file:
# frozen_string_literal: true
It is usually placed after the shebang line if there is one:
#!/usr/bin/env ruby
# frozen_string_literal: true
Example without frozen string literals
message = "hello"
message << " world"
puts message
Output:
hello world
This works because the string literal is mutable.
Example with frozen string literals
# frozen_string_literal: true
message = "hello"
message << " world"
Output:
FrozenError
Ruby raises an error because "hello" is frozen.
Creating a mutable copy
If you need to modify the string, create a new mutable copy:
# frozen_string_literal: true
message = "hello".dup
message << " world"
puts message
Output:
hello world
Explicitly freezing without the magic comment
Even without the file-level comment, you can freeze a single string:
message = "hello".freeze
The magic comment is just a file-wide shortcut for string literals.
Step by Step Execution
Consider this Ruby file:
# frozen_string_literal: true
greeting = "hi"
name = "Sam"
result = greeting + ", " + name
puts result
Step by step
1. Ruby reads the magic comment
# frozen_string_literal: true
Ruby enables frozen string literals for this file.
2. Ruby creates greeting
greeting = "hi"
Because the file has frozen string literals enabled, "hi" is frozen.
3. Ruby creates name
name = "Sam"
"Sam" is also frozen.
4. Ruby builds result
result = greeting + ", " + name
This works. The + operator creates a new string rather than modifying greeting directly.
So frozen strings can still be used in expressions.
5. Ruby prints the result
puts result
Output:
hi, Sam
What would fail?
This would fail:
# frozen_string_literal: true
greeting = "hi"
greeting << " there"
Why? Because << tries to modify the original string object, and frozen objects cannot be changed.
Real World Use Cases
# frozen_string_literal: true is useful in many real Ruby programs.
Generated files
Tools like Bundler often generate scripts containing many fixed strings:
- gem names
- file paths
- environment variable keys
- command names
Those strings are not meant to be edited at runtime, so freezing them is sensible.
Libraries and gems
Library code often uses many constant-like strings:
JSON_CONTENT_TYPE = "application/json"
Freezing literals helps avoid accidental mutation.
Rails apps
Rails applications may use many strings for:
- parameter keys
- route names
- SQL fragments
- logging messages
- configuration values
Freezing literals can reduce accidental bugs and encourage safer string handling.
CLI scripts
Command-line tools often define fixed option names and messages:
puts "Usage: my_tool [options]"
Those strings are ideal candidates for freezing.
Performance-sensitive code
In code that creates many repeated string literals, immutability can reduce unnecessary object mutation and sometimes improve memory behavior.
Real Codebase Usage
In real projects, developers use frozen string literals as part of a broader style of writing safer Ruby.
Common pattern: immutable defaults
# frozen_string_literal: true
DEFAULT_ROLE = "guest"
STATUS_OK = "ok"
These values are intended to stay unchanged.
Common pattern: duplicate before mutation
If a value starts from a literal but needs editing, duplicate it first:
# frozen_string_literal: true
path = "users/".dup
path << "123"
Common pattern: use interpolation or + for new strings
Instead of mutating an existing literal:
full_name = first_name + " " + last_name
or:
full_name = "#{first_name} #{last_name}"
These create new strings rather than changing a frozen literal.
Validation and error messages
Developers often store fixed messages safely:
# frozen_string_literal: true
ERROR_MESSAGE = "Invalid API token"
Guarding against accidental mutation
Without frozen literals, helper methods can accidentally change a shared string literal and create confusing bugs. Enabling frozen literals makes such mistakes fail fast with FrozenError, which is easier to debug.
Common Mistakes
Mistake 1: Thinking all strings become frozen
This comment does not freeze every string in Ruby.
# frozen_string_literal: true
a = "hello"
b = String.new("hello")
puts a.frozen? # true
puts b.frozen? # false
Mistake 2: Trying to append to a literal directly
Broken code:
# frozen_string_literal: true
text = "abc"
text << "def"
This raises FrozenError.
Fix it like this:
# frozen_string_literal: true
text = "abc".dup
text << "def"
Mistake 3: Confusing + with <<
+ creates a new string. << mutates the existing string.
# frozen_string_literal: true
a = "hi"
b = a + " there" # works
#a << " there" # would fail
Mistake 4: Putting the comment in the wrong place
The magic comment should be near the top of the file.
Typical form:
#!/usr/bin/env ruby
# frozen_string_literal: true
If it is buried deep in the file, Ruby may not treat it as a magic comment.
Mistake 5: Assuming older code will always work unchanged
Older Ruby code sometimes mutates literals freely:
message = "Error: "
message << details
With frozen string literals enabled, this pattern may break. Use dup, interpolation, or build a new string instead.
Mistake 6: Forgetting file-level scope
Adding the comment to one file does not affect another file. Each file controls its own string literal behavior.
Comparisons
| Concept | What it does | Mutable? | Scope |
|---|---|---|---|
| Normal string literal | "hello" creates a regular string literal | Usually yes | Current object |
# frozen_string_literal: true | Freezes string literals in the file | No for literals in that file | Current file |
"hello".freeze | Freezes one specific string object | No | That object only |
String.new("hello") | Creates a new string object | Yes by default | That object only |
+ with strings | Creates a new combined string | Result is usually mutable unless frozen later | Expression result |
<< with strings | Modifies the existing string | Requires mutable string | Existing object |
+ vs <<
# frozen_string_literal: true
a = "hi"
b = a + " there" # works
#a << " there" # FrozenError
- Use
+when you want a new string - Use
<<only when the receiver is mutable and you intentionally want to change it
Magic comment vs .freeze
Use # frozen_string_literal: true when you want file-wide behavior.
Use .freeze when you want to freeze a specific object only.
Cheat Sheet
# At the top of a file
# frozen_string_literal: true
What it means
- Freezes string literals in that Ruby file
- Helps prevent accidental mutation
- Can improve memory/performance in some cases
- Applies per file, not globally
Affected
# frozen_string_literal: true
s = "hello"
s.frozen? # => true
Not automatically affected
# frozen_string_literal: true
s = String.new("hello")
s.frozen? # => false
Safe operations on frozen strings
name = "Ruby"
name.upcase # returns a new string
name + "!" # returns a new string
"#{name} rocks" # creates a new string
Operations that fail
name = "Ruby"
name << "!" # FrozenError
name.gsub!("R", "B") # FrozenError
How to modify anyway
name = "Ruby".dup
name << "!"
Remember
+creates a new string<<mutates existing string- magic comment affects literals in one file only
- put the comment near the top of the file
FAQ
What does # frozen_string_literal: true do in Ruby?
It tells Ruby to freeze string literals in that file, making them immutable.
Does it freeze every string in my program?
No. It only affects string literals in the current file.
Why do Bundler or generated files include it?
Generated files often contain many fixed strings that should not be modified, so freezing them is safe and useful.
What error happens if I modify a frozen string?
Ruby raises FrozenError.
Can I still combine frozen strings?
Yes. Operations like + and interpolation create new strings, so they still work.
How do I make a frozen string editable?
Use dup to create a mutable copy:
editable = "hello".dup
Is this the same as calling .freeze everywhere?
It has a similar effect for literals, but the magic comment applies automatically to all string literals in that file.
Should I always use # frozen_string_literal: true?
It is common and helpful in many Ruby codebases, but you should understand that code mutating literals may need to be updated first.
Mini Project
Description
Create a small Ruby script that builds status messages for a command-line app while using # frozen_string_literal: true. This project demonstrates the difference between reading fixed string literals and trying to mutate them. It also shows how to correctly create editable copies when needed.
Goal
Build a Ruby script that uses frozen string literals safely and produces formatted output without raising FrozenError.
Requirements
- Add
# frozen_string_literal: trueat the top of the file. - Define at least two fixed string literals for labels or message prefixes.
- Build a final message using safe string operations.
- Create one mutable copy with
dupand modify it. - Print the final results to the console.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.