# Revise 3.16: fast type-revision, frozen worlds, and complete language support

**URL:** <https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211>\
**Category:** Package Announcements\
**Tags:** revise\
**Created:** [July 15, 2026, 9:04am UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211 "2026-07-15T09:04:13Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [July 15, 2026, 9:04am UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/1 "2026-07-15T09:04:13Z")

</div>

A month ago I [announced Revise 3.15.0](https://discourse.julialang.org/) and described a big push through the issue backlog. That push has continued, and I’m pleased to announce Revise 3.16.1 (together with its recent siblings 3.15.1 and 3.16.0). First, the numbers: since June 10, there have been

- **26 PRs merged** across three releases
- **34 issues closed** , all but one of which predated the previous announcement. The oldest was filed in July 2020, and the median had been open for about two and a quarter years.
- **Open issues dropped from 41 to 8.**

I’m pretty excited that a package of Revise’s complexity has gotten down to just 8 open issues. Moreover, most of the survivors are design questions or feature requests rather than bugs.

## `struct` revision: headed for on-by-default

The change I suspect will excite users most: `struct` revision is now fast enough to turn on by default. On Julia 1.12 and later, Revise has supported `struct` revision since 3.13.0, but so far this has been off by default because the bookkeeping could take a very long time. It’s still off-by-default, but the performance barrier is now gone ([#1076](https://github.com/timholy/Revise.jl/pull/1076)). The scan that discovers code affected by a type change used to walk the type tree recursively, rescanning the names of every loaded module at each step; it is now a single sweep over module bindings. In a session with ~29,000 types, the cost dropped from ~5.5 seconds to ~13 millisecond, and sessions with large type trees had been reporting _minutes_ ([#988](https://github.com/timholy/Revise.jl/issues/988)) on workloads that are now ~40ms. The rewrite also eliminated a race that produced spurious world-age warnings ([#993](https://github.com/timholy/Revise.jl/issues/993), [#925](https://github.com/timholy/Revise.jl/issues/925)).

With the performance barrier gone, I intend to make `struct` revision the default in Revise 3.17.0. **Think of 3.16.x as the release candidate** : if you’re on Julia 1.12 or later, please opt in now and report anything that misbehaves. To enable it, add (or edit) a `LocalPreferences.toml` file next to the `Project.toml` of your active project or default environment, containing:

```toml
[Revise]
revise_structs = true

```

See [the documentation](https://timholy.github.io/Revise.jl/stable/config/#Enabling-struct-revision) for details.

## Revise now runs in a “frozen world”

Another very exciting change is that Revise now runs in a “frozen world” ([#1092](https://github.com/timholy/Revise.jl/pull/1092)). Julia’s [world age mechanism](https://docs.julialang.org/en/v1/manual/worldage/#The-World-Age-mechanism) means that loading new packages, or revising code, can invalidate previously-compiled code. When the invalidated code was Revise’s _own_ machinery, the results ranged from sluggishness (Revise recompiling itself after you load a big package) to outright failure: the notorious “method too new” and “binding accessed prior to its definition world” errors that could appear when Revise ran mid-way through a half-applied update. CUDA and Plots users were hit especially often ([#552](https://github.com/timholy/Revise.jl/issues/552), [#607](https://github.com/timholy/Revise.jl/issues/607), [#701](https://github.com/timholy/Revise.jl/issues/701), [#746](https://github.com/timholy/Revise.jl/issues/746), [#807](https://github.com/timholy/Revise.jl/issues/807)).

Now, Revise’s internals (signature extraction, method/type deletion and insertion) run pinned to the world age captured when Revise was loaded, so nothing you load or revise afterward can invalidate them or confuse their dispatch. Your code, of course, is still evaluated at the latest world thanks to per-frame world support in JuliaInterpreter 0.11. This eliminates a whole class of failures that have plagued Revise for years. (For the rare soul who deliberately revises Revise itself or one of its dependencies, `Revise.advance_world!()` re-pins to the current world.)

Nearly all of the serious work behind this feature actually landed upstream, in [JuliaInterpreter 0.11.0](https://github.com/JuliaDebug/JuliaInterpreter.jl/releases/tag/v0.11.0): a _long_ sequence of PRs finally delivered proper world-age support to JuliaInterpreter. Interpreted code now follows the same world-age semantics as compiled code—each frame executes at a well-defined world—where previously every interpreted call behaved like `Base.invokelatest`. That’s a long-desired feature in its own right, and its benefits extend to Debugger.jl and the other tools built on JuliaInterpreter.

## More faithful revisions

Several fixes close gaps where a revision was silently incomplete:

- **Files included into more than one module.** If the same file is `include`d into several modules, editing it now updates _all_ of them; previously only the first was revised ([#730](https://github.com/timholy/Revise.jl/issues/730), from 2023).
- **`include(mapexpr, file)`.** Transforms passed to the two-argument form of `include`(a feature used by DispatchDoctor among other packages) now re-applied on revision instead of being silently dropped ([#634](https://github.com/timholy/Revise.jl/issues/634), from 2021; [#820](https://github.com/timholy/Revise.jl/issues/820)). This work required Julia changes ([#62176](https://github.com/JuliaLang/julia/pull/62176)) so will only work on Julia 1.14+.
- **Removing an `export`.** Deleting an `export` statement now retracts the export ([#633](https://github.com/timholy/Revise.jl/issues/633), from 2021). This also relies on new Julia support ([#62131](https://github.com/JuliaLang/julia/pull/62131)) and so requires Julia 1.14+.
- **Revised types inside `Union`s.** Methods whose signatures mention a revised struct only through a `Union{Foo,Bar}` were previously left dispatching on the stale type ([#932](https://github.com/timholy/Revise.jl/issues/932)).
- **Sharper diffing of macro-generated code.** Revise’s expression comparator used to consider _any_ generated (`#...`) name equal to any other, so a change confined to such names was invisible. Names are now matched by base name with consistent pairing ([#1086](https://github.com/timholy/Revise.jl/pull/1086)).

The multiple-includes and support for `include(mapexpr, file)` may be a noteworthy milestone on their own: with those done, Revise may be able to claim that if your package precompiles, Revise supports it. That doesn’t mean that Revise doesn’t have bugs or [limitations](https://timholy.github.io/Revise.jl/stable/limitations/), but at least it now faithfully represents the relationships between source files and the packages that get built from them.

## New capabilities

- **`Revise.stale_load("MyPkg")`.** Suppose you’ve edited a package’s source but you’d rather not wait for re-precompilation: `stale_load` loads the most recent (stale) precompile cache and then revises just the files that changed. For packages that are expensive to compile, this can be a big win ([#738](https://github.com/timholy/Revise.jl/issues/738)).
- **`@includet`.** The `includet` _function_ has always evaluated files into `Main`, because a function cannot know its caller’s module. The new `@includet` macro evaluates into the calling module, just as `include` does ([#682](https://github.com/timholy/Revise.jl/issues/682)). Relatedly, `includet` now returns the value of the file’s last expression, matching `include` ([#783](https://github.com/timholy/Revise.jl/issues/783)).
- **Early warning for duplicate methods.** Defining the same method signature in two places within a package works fine in a live session but breaks the _next_ precompilation. Revise now warns as soon as a revision creates this situation, rather than letting you discover it at your next restart ([#889](https://github.com/timholy/Revise.jl/issues/889)).

## Robustness against code generators

Investigating [#945](https://github.com/timholy/Revise.jl/issues/945) (a code generator that deletes and rewrites entire source directories) uncovered two failure modes that could bite anyone:

- **Files that vanish and reappear.** A `revise()` landing while a file was temporarily deleted used to permanently delete all its methods and drop it from tracking. Now there’s a grace period (`Revise.missing_file_grace[]`, default 5 seconds) during which the file is left alone, and even past the grace period the file stays registered so its return is detected ([#1074](https://github.com/timholy/Revise.jl/pull/1074)).
- **Rewrites faster than the filesystem clock.** File timestamps advance in coarse ticks (~10ms on Linux, worse under virtualization), so a very fast delete-and-rewrite could produce an identical timestamp and the change was lost. Revise now trusts the filesystem _event_ (which names the changed file) rather than relying on timestamp comparison ([#1075](https://github.com/timholy/Revise.jl/pull/1075)). In stress testing with thousands of rapid rewrite cycles, losses went from roughly 1-in-700 to zero.

## Latency

- `revise()` no longer sleeps before processing changes. The pause was there to let editors finish writing, but with other improvements to synchronization the sleep was pure added latency ([#838](https://github.com/timholy/Revise.jl/issues/838), from 2024).

## Try it, and please report any problems

With the backlog down to single digits, “just works” is closer than it has ever been, and to me Revise now feels basically done. It’s a great time to test it out and report any bugs that harm the user experience, so that we’re well-prepared for an eventual rewrite around JuliaLowering and partial migration to Base.

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [July 15, 2026, 9:29am UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/2 "2026-07-15T09:29:58Z")

</div>

Absolutely fantastic, what an impressive achievement! I can’t wait to try it out. Fast automatic struct revision is going to be so convenient - and I’ve also been hit my several of the now-closed bugs so the extra stability is going to be a nice QoL improvement.

---

<div class="post-metadata">

**Author:** ![marteaua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marteaua/32/214927_2.png) [@marteaua](https://discourse.julialang.org/u/marteaua)\
**Post date:** [July 15, 2026, 10:08am UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/3 "2026-07-15T10:08:58Z")

</div>

This is major, thank you very much!

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [July 15, 2026, 11:03am UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/4 "2026-07-15T11:03:02Z")

</div>

Incredible. Now I don’t think there is any motivation _not_ to use it. Is Revise going to become a stdlib, or supported more prominently in some other way?

---

<div class="post-metadata">

**Author:** ![MDSW](https://avatars.discourse-cdn.com/v4/letter/m/94ad74/32.png) [@MDSW](https://discourse.julialang.org/u/MDSW)\
**Post date:** [July 15, 2026, 12:36pm UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/5 "2026-07-15T12:36:59Z")

</div>

This is really important for emerging use cases for Julia; very much appreciate the continued focus on making this robust and on-by-default!

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [July 15, 2026, 1:38pm UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/6 "2026-07-15T13:38:52Z")

</div>

My view is that when JuliaLowering migrates into Base, the right move is to rewrite the entire Revise stack around it, moving some bits into Base itself, and the rest of Revise into a stdlib. Whatever does happen will have to reflect some degree of consensus among Julia committers, of course.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [August 31, 2026, 1:10pm UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/7 "2026-08-31T13:10:33Z")

</div>

Just a quick update: Revise 3.17.0 is out, and **struct revision is on by default**. You can opt-out (see the docs) if it’s causing you problems, but I’ve been using it for a while and haven’t noticed issues. (That said, I don’t revise structs all that often.) Something that I think may be more likely to cause trouble is a new warning about switching package versions (which tend to be the revisions most likely to go wrong); I’ll be curious to hear feedback about whether this is a net win or just annoying.

As always, please report bugs or other problems to the issue tracker: [Issues · timholy/Revise.jl · GitHub](https://github.com/timholy/Revise.jl/issues)

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [August 31, 2026, 1:42pm UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/8 "2026-08-31T13:42:02Z")

</div>

> [@tim.holy](#):
>
> As always, please report bugs

Done: [Adding a field to a struct (with revise\_structs = true) silently orphans previously defined outer constructors · Issue #1133 · timholy/Revise.jl · GitHub](https://github.com/timholy/Revise.jl/issues/1133)

Thanks for the good work!

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [September 4, 2026, 3:33pm UTC](https://discourse.julialang.org/t/revise-3-16-fast-type-revision-frozen-worlds-and-complete-language-support/138211/9 "2026-09-04T15:33:14Z")

</div>

So I’m implementing new types in my project today. Hence I heavily used the new `struct` revsion by default. It just worked, no issues at all, not even warnings, and it was instantaneous. Wonderful job, I really love the interactivity of Julia and Revise today 🙌 🙌 🙌 !
