Mojo 1.0 is the right point to migrate code that still targets the pre-1.0 language. Start by pinning Mojo 1.0.0 on a branch, compile every entry point, then address the explicit declaration, collection, string, pointer, and package-format changes in small commits. Do not assume that every standard-library API is stable simply because the language reached 1.0.
Released on August 11, 2026, Mojo 1.0 establishes a source-compatibility policy for stable language features and APIs. It also intentionally contains a larger cleanup than future 1.x releases are expected to: old names remain as deprecated aliases in many cases, and compiler diagnostics often include a fix-it, but several behaviors are stricter or have been removed.
This guide turns the release notes into a practical migration sequence for applications and libraries moving from Mojo 0.26 or the 1.0 betas.
- What Mojo 1.0 Changes for Existing Projects
- Create a Reproducible Upgrade Branch
- Fix Declarations Before Renaming APIs
- Update Collections Without Changing Semantics
- Rename String and Lifecycle APIs
- Review Pointer Code as a Safety Change
- Rebuild Precompiled Packages
- Run a Focused Migration Test Matrix
- Understand What “Stable” Means in Mojo 1.0
- A Safe Merge Checklist
- Official References
What Mojo 1.0 Changes for Existing Projects
The migration is less about adopting every new feature and more about making intent explicit. The most common changes fall into six groups:
| Pre-1.0 code or behavior | Mojo 1.0 direction | Why it matters | |—|—|—| | First assignment implicitly declares a variable | Declare it with var | Implicit declarations are deprecated and produce a fix-it | | InlineArray and .size | Array and .length | Collection terminology is now consistent | | StringSlice | StringSpan | Non-owning views now use the same “span” vocabulary | | ImplicitlyDestructible and __del__() | Deinitable and __deinit__() | Lifecycle naming now matches initialization | | UnsafePointer plus unprefixed unsafe operations | Pointer plus unsafe_* operations | Unsafety is attached to the operation rather than the whole type | | mojo package and .mojopkg | mojo precompile and .mojoc | Precompiled package tooling and format names changed |
There are behavioral changes too. A list expression now creates a fixed-size Array by default rather than a heap-allocated List. Invalid contiguous slices no longer clamp or wrap, negative indexes are not supported, and references into containers can be invalidated by mutations that may reallocate storage.
These are exactly the kinds of changes that deserve tests around behavior, not only a clean compile.
Create a Reproducible Upgrade Branch
Keep the old toolchain reproducible before changing source code. Record the current Mojo version and create a dedicated migration branch:
mojo --version
git switch -c upgrade/mojo-1.0
git status --short
On a supported macOS or Linux environment, install the stable release with uv. Pinning 1.0.0 makes the first migration run repeatable:
uv venv
source .venv/bin/activate
uv pip install "mojo==1.0.0"
mojo --version
The official package is not available for native Windows. Windows developers should use WSL on a supported Linux distribution rather than expecting the wheel to install directly in PowerShell.
If the project also uses MAX, upgrade and test it separately. Mojo 1.0 moved several accelerator-focused APIs from the standard library to the max Mojo package, and the layout package now ships with MAX rather than Mojo. Keeping language migration and MAX dependency changes in separate commits makes failures easier to isolate.
Fix Declarations Before Renaming APIs
Start with compiler diagnostics that have narrow, mechanical fixes. Explicit variable declarations are a good first pass.
Before:
def main():
count = 3
message = "ready"
print(message, count)
After:
def main():
var count = 3
var message = "ready"
print(message, count)
The rule applies to the first assignment to a name, including a walrus target or a standalone type annotation. Binding forms that already describe the binding—such as a for target or with ... as target—do not need an added var.
Also update bare keyword variadics from **kwargs to var **kwargs. The convention was already mutable in practice; Mojo 1.0 now requires the source to say so explicitly.
Compile after this pass before doing broad search-and-replace work:
mkdir -p build
mojo build src/main.mojo -o build/app
Treat compiler fix-its as reviewable patches. Apply them to a clean branch, inspect the diff, and recompile rather than accepting a large automated rewrite without review.
Update Collections Without Changing Semantics
The InlineArray rename is usually mechanical:
var ports: InlineArray[Int, 3] = [8000, 8001, 8002]
print(ports.size)
becomes:
var ports: Array[Int, 3] = [8000, 8001, 8002]
print(ports.length)
The old type and member remain as deprecated aliases for transition, but migrating now prevents warnings from accumulating. Named parameters also changed from ElementType and size to T and length.
More importantly, audit unannotated list expressions. In Mojo 1.0, this value is an Array, not a List:
var fixed_values = [1, 2, 3]
If the code needs a growable heap collection, state that intent:
var dynamic_values: List[Int] = [1, 2, 3]
dynamic_values.append(4)
Check Slice and Reference Behavior
Search for negative indexes and permissive slices at the same time. Code such as values[:-1] is no longer a portable shortcut. Calculate a non-negative end explicitly and cover the empty-container case:
var end = max(len(dynamic_values) - 1, 0)
var prefix = dynamic_values[:end]
Mojo 1.0 also rejects a reference that is held across a container mutation when the mutation can invalidate the referenced element:
var values: List[Int] = [1, 2, 3]
ref first = values[0]
values.append(4)
print(first)
Do not work around that diagnostic with an untracked lifetime. Consume the reference before mutation, copy the value when that matches the data model, or reacquire the reference after the mutation.
Rename String and Lifecycle APIs
Replace StringSlice with StringSpan. Both describe a non-owning view into encoded string data, but StringSpan is the 1.0 name and aligns with Span, ImmStringSpan, and MutStringSpan.
String iteration deserves a behavior test: iterating over a String, StringSpan, or StringLiteral now yields user-perceived grapheme clusters by default. Code that needs Unicode scalar values should use codepoints() or codepoint_slices(), while byte-oriented parsers should use bytes().
For lifecycle code, migrate:
ImplicitlyDestructibletoDeinitable;__del__()to__deinit__();- the
readargument convention toimm.
These older spellings generally remain as deprecated aliases, which makes a staged migration possible. Still, update them before enabling warnings as errors so deprecation output does not hide new problems.
Review Pointer Code as a Safety Change
Mojo 1.0 unifies Pointer and UnsafePointer. A pointer to an existing value can use the safe form:
def show_addressed_value():
var value = 42
var ptr = Pointer(to=value)
print(ptr[])
Operations that can bypass bounds or lifetime guarantees now make that risk visible in the method or keyword name. Typical migrations include load() to unsafe_load(), store() to unsafe_store(), pointer addition to unsafe_offset(), and free() to unsafe_free().
Do not perform a blind textual rename across allocator code. Verify these properties for each pointer path:
- who owns the allocation;
- how many elements are initialized;
- which origin is attached to the pointer;
- whether an offset is bounds-checked;
- where element destruction and deallocation occur.
The compatibility aliases share layouts, so much code will continue compiling. That is useful for migration, but it is not evidence that the ownership model is correct.
Rebuild Precompiled Packages
Projects that produced .mojopkg files should update both the command and artifact name:
mojo precompile src/acme -o build/acme.mojoc
A .mojoc file is tied to the compiler version that produced it. Rebuild precompiled dependencies with Mojo 1.0 instead of copying artifacts from an older toolchain into the upgraded environment.
Update CI cache keys to include the complete Mojo version. Also inspect import paths: source packages, .mojoc packages, source modules, and legacy .mojopkg files now follow an explicit resolution order. Leaving stale artifacts beside source can make a local build and a clean CI build select different inputs.
Run a Focused Migration Test Matrix
A clean build is the beginning, not the end. Run the project’s executable test files with the 1.0 runtime and add focused cases for behavior that changed:
mojo run tests/test_collections.mojo
mojo run tests/test_strings.mojo
mojo run tests/test_ownership.mojo
mojo build src/main.mojo -o build/app
At minimum, verify:
- empty and boundary slices;
- any code that previously used negative indexes;
- string iteration with combining characters and emoji;
- container references around
append(),pop(), or insertion; - pointer allocation, initialization, destruction, and release paths;
- Python interop operators and error propagation;
- rebuilt
.mojocpackages in a clean environment; - GPU imports and fixed-width kernel argument types if the project uses MAX.
For GPU code, note one especially important boundary: Int and UInt no longer conform to DevicePassable. Use a fixed-width type such as Int32 for values passed to accelerator kernels, and review APIs that moved from std.gpu to max.gpu.
Understand What “Stable” Means in Mojo 1.0
Mojo 1.0 follows semantic versioning for stable language features and standard-library APIs explicitly marked stable. The language’s source-level stability is broader than the standard library’s: unmarked library APIs are still considered unstable, and the Mojo ABI is not stable.
That distinction should influence dependency policy:
- pin the exact compiler version for reproducible builds and
.mojocartifacts; - check API documentation for a “Stable since 1.0.0” marker;
- avoid compiler-internal names beginning with double underscores unless the manual documents them as stable;
- treat
asyncandawaitas unstable for now; - read release notes before every minor upgrade, even during the 1.x series.
Mojo 1.0 is a much stronger foundation, but it is not a promise that every experimental feature or library member will remain unchanged.
A Safe Merge Checklist
Before merging the migration branch:
- Confirm CI and developer environments report the same
mojo --version. - Remove implicit declarations and review every compiler fix-it.
- Make
ArrayversusListchoices explicit where mutation matters. - Replace deprecated string, lifecycle, pointer, and package spellings.
- Rebuild every
.mojocartifact with the pinned compiler. - Test boundary slices, Unicode iteration, and container-reference invalidation.
- Test Python, C, MAX, and GPU boundaries used by the project.
- Run the build in a clean environment without old caches or precompiled packages.
- Keep the old production artifact available for rollback.
The safest Mojo 1.0 upgrade is a sequence of small, reviewable transformations backed by behavior tests. Let the compiler identify syntax and naming changes, but use your test suite to prove that stricter collection, string, pointer, and accelerator rules preserve the application’s intent.


Comment