Proposal: General “Test” vs “Production” Build Modes

I’d like to propose introducing a general build mode distinction in Mojo: test vs production. The idea is to provide developers with flexible workflows during development and experimentation, while ensuring consistency and safety in production.

1. Test Mode

  • Optional pedantic checks are relaxed (e.g., missing raises can be inferred and warned).

  • Compilation prioritizes speed and iteration, enabling rapid AI-assisted or human prototyping.

  • Minor warnings (formatted as JSON) are issued instead of hard errors, including suggestions on how to fix/avoid them

  • Ideal for interactive development, experimentation, and AI-assisted code generation.

2. Production Mode (default)

  • All checks are strictly enforced (explicit raises, type correctness, ownership rules).

  • Full optimization passes are enabled.

  • Ensures deterministic, safe behavior across deployments.

  • Guarantees ecosystem consistency, avoiding fragmentation.

Benefits

  • Accelerates AI-assisted workflows, preventing trivial errors from blocking iteration.

  • Supports rapid human prototyping without compromising long-term reliability.

  • Maintains ecosystem integrity in production builds.

  • Could serve as a foundation for additional developer-friendly flags (e.g., --infer-raises) without introducing permanent semantic divergence.

I believe this pattern could complement the current proposal to remove fn and make def explicit for raises, providing a smooth path for experimentation and testing, while keeping production code safe and predictable by default.

While I think that “debug” and “production” modes are valuable to have, I think that making them do something which makes some source code compile in one mode and not in other modes is a bad idea. Forcing raises to be annotated also reduces compile times because the compiler doesn’t have to infer them, which can actually be quite difficult to do properly given how complex types can get in Mojo. Trying to infer types in Mojo would actually massively increase compile times, with complex situations easily being able to take hours to compile. Swift has a bunch of 5-20 line snippets which the compiler aborts trying to compile after 15 minutes for the same reasons. Ownership rules are used for enforcing correctness, so those can’t be disabled unless you’re willing to accept a language which is much, much harder to write code in, since due to how Mojo works disabling origins would mean all memory from standard library types would be free instantly, forcing everyone to go back to raw pointers if they want to use the heap.

I think the best we can offer is debug being a shorthand for something like mojo build -O1 -g2 -fsanitize=address ....

I think that getting some feedback that your code doesn’t work only after you’ve managed to make it work in debug mode would lead to a lot of developer friction since you’d then be forced to do iteration in the “strict” mode which has heavy compiler optimizations instead of being able to do that iteration in a debug mode which doesn’t request those optimizations.

Having flags that make code compile in one mode and not another are not new - treat warnings as errors being one example

If there is no such flag to infer raises, what you will get is that AI learns to always put “raises” - because it works in more cases. Then the compiler will need to check anyway, for “raises” to mean anything.

Perhaps in debug mode a more “quick and dirty” check could be enabled - like a first order raises inference, without fully evaluating possible types.