Let's start with the elephant in the room. As of March 2025, Modular officially deprecated the REPL. They walked it back after community backlash, but the writing's on the wall - interactive development isn't their priority anymore.
This isn't just about losing a cool feature. The REPL was how most of us debugged our Mojo code, tested snippets, and figured out why our "simple" matrix multiplication was segfaulting. Now we're back to the stone age of print-debugging and recompiling entire binaries to test a one-line change.
What Actually Broke When the REPL Died
No more interactive debugging. You can't just fire up a REPL, import your broken function, and poke at it until you understand what's going wrong. Everything has to be a complete program now.
Docstring tests are gone. All those nice examples in your code documentation? They don't run anymore. The testing framework used to execute them via the REPL, so now they're just pretty comments.
Learning curve became a cliff. New developers could experiment with Mojo syntax interactively. Now they have to write complete programs and deal with the compiler's cryptic error messages from day one.
No more calculator mode. Python developers use the REPL as a smart calculator. Need to check [math.pow(2, 64)](https://docs.python.org/3/library/math.html#math.pow)
? Fire up Python and try it. With Mojo, you're back to writing throwaway .mojo
files.
The VS Code Extension: Your Only Real IDE Option
The VS Code extension is literally your only choice if you want any IDE support at all. Here's what actually works:
Syntax highlighting: Works fine. Basic but functional.
Debugging: Works when the stars align. The LLDB integration is real, but good luck when it shows you MLIR intermediate representation instead of your actual code.
Autocompletion: Exists for basic types. Breaks spectacularly on parameterized functions and anything involving generics.
Error reporting: Shows you cryptic compiler errors inline, which is better than nothing but not by much.
What doesn't work: refactoring tools, go-to-definition for complex types, intelligent code suggestions, or basically anything you'd expect from a modern IDE.
Setting Up a Development Environment That Won't Drive You Insane
Here's what I've learned after 8 months of daily Mojo development:
1. Keep Python Around for Everything
Don't go all-in on Mojo. Keep your data loading, business logic, and anything that doesn't need performance in Python. Use Mojo only for the hot paths that you've actually profiled.
## Project structure that doesn't suck
project/
├── python_src/ # All your business logic
│ ├── data_loading.py
│ ├── preprocessing.py
│ └── main.py
├── mojo_kernels/ # Only the performance-critical stuff
│ ├── matrix_ops.mojo
│ └── custom_loss.mojo
└── tests/
├── test_python_logic.py
└── test_mojo_kernels.mojo
2. Build a Debugging Workflow That Actually Works
Since the REPL is dead, here's how to debug without losing your mind:
Use `breakpoint()` everywhere. The builtin breakpoint() function is your best friend. When VS Code debugging fails (and it will), programmatic breakpoints work.
Keep a scratch.mojo file. Create a throwaway file where you can test snippets quickly. Much faster than setting up a full project structure for every experiment.
Log everything. Print statements are primitive but they work. MLIR errors are useless, but seeing your actual data values helps.
from testing import assert_equal
fn debug_matrix_multiply():
var a = Matrix[Float32](2, 3)
var b = Matrix[Float32](3, 2)
# Fill with test data
for i in range(a.rows):
for j in range(a.cols):
a[i, j] = Float32(i * a.cols + j)
print("Matrix A:")
print(a) # Actually show what your data looks like
breakpoint() # Stop here to inspect state
var result = a @ b
print("Result shape:", result.rows, "x", result.cols)
3. Testing Without Docstrings
Since docstring tests are dead, here's how to actually test your Mojo code:
Use dedicated test files. Create test_yourmodule.mojo
files with explicit test functions.
Steal from the standard library. Look at Mojo's own test files for patterns that actually work.
Keep tests simple. Complex test setups break in weird ways. Write lots of small, focused tests instead of comprehensive integration tests.
4. Development Workflow for Hybrid Projects
This is what actually works when you're mixing Python and Mojo:
- Prototype everything in Python first. Get the algorithm working with numpy/torch.
- Profile to find bottlenecks. Use
cProfile
to identify what's actually slow. - Port only the hot path to Mojo. Don't rewrite everything, just the 10% that matters.
- Keep Python fallbacks. When (not if) your Mojo code breaks, you need a working Python version.
- Test both versions. Make sure Python and Mojo implementations give the same results.
Error Messages That Make You Question Reality
Let's talk about the real development experience. When your Mojo code breaks, you get errors like this:
error: 'linalg.generic' op operand #0 does not dominate this use
%2 = linalg.generic {indexing_maps = [#map], iterator_types = ["parallel"]}
^
What the fuck does that mean? Here's what I've learned:
MLIR errors are compiler internals. They're not meant for human consumption. The compiler is showing you its internal representation, not your actual code.
Line numbers are lies. The error points to the MLIR IR, not your source code. The actual bug could be anywhere.
Context is everything. Start with the simplest possible code and add complexity one line at a time. When it breaks, you know the last line caused it.
Discord is your debugger. The Modular Discord has humans who can translate MLIR gibberish into actual programming concepts.
The debugging experience is getting better (supposedly), but right now it's like debugging assembly code while blindfolded. Plan for frustration.
Here's a fun one that took us 4 hours to debug: Mojo 24.4 has a bug where using @parameter
with certain generic types causes silent memory corruption. The error only shows up when you run the same code path 1000+ times. Version 24.5 fixed it, but broke something else with SIMD alignment.
Now let's dig into the specific tools and workflows that might save your sanity when dealing with production deployments.