Skip to content

dart:mirrors deprecation and discontinuation #44489

Open

Description

@mit-mit

The Dart core library dart:mirrors is being formally deprecated starting in the November 2026 stable release (Dart 3.14), with complete removal planned for Dart 4.0.

Status

dart:mirrors has long been documented as experimental and unstable:

As of Dart SDK commit 541360, dart:mirrors has been annotated with @deprecated on main.

Rationale

Making dart:mirrors a stable, supported core library across all targets is fundamentally incompatible with the architecture and goals of modern Dart:

  1. AOT Compilation & Tree Shaking: Dart relies heavily on global static whole-program analysis and dead code elimination to produce small, fast binaries. Full runtime reflection requires retaining arbitrary metadata and executable code that could theoretically be reached dynamically, preventing effective tree shaking.
  2. Platform Portability: Dart's commitment is cross-platform portability across native architectures (AOT/JIT), Web (JavaScript), and WebAssembly (Wasm). A reflection mechanism that cannot be ported to Web, Wasm, or Flutter fractures the ecosystem and limits code sharing.
  3. Language Evolution Drift: The dart:mirrors API was designed during Dart 1. It does not model modern Dart features (sound null safety, extension methods, extension types, records, pattern matching), and maintaining parity with ongoing language features would require significant runtime overhead and compiler complexity.

Timeline & Next Steps

  • November 2026 Stable Release (Dart 3.14): dart:mirrors is marked @deprecated. Compiling code that imports dart:mirrors will emit deprecation warnings, but code will continue to execute on the standalone Dart VM.
  • Dart 4.0: dart:mirrors will be removed entirely from the Dart SDK.

Recommended Alternatives & Migration

In auditing remaining dart:mirrors usages across the ecosystem, most call sites do not require full runtime reflection and can migrate to simpler standard library or static analysis APIs:

  • Resolving Package or Script Paths: Replace currentMirrorSystem().findLibrary(...) or ClosureMirror.function.location file lookups with Isolate.resolvePackageUriSync(Uri.parse('package:foo/foo.dart')) (dart:isolate) or Platform.script (dart:io).
  • Static Code Inspection (Tests & Tooling): Replace reflectClass / LibraryMirror declaration checks in tests and linters with package:analyzer (parseString from package:analyzer/dart/analysis/utilities.dart for fast single-file AST inspection, or AnalysisContextCollection when resolved element types are needed).
  • Enums, Dispatch & Test Runners: Replace reflective enum iteration and string-based method dispatch (InstanceMirror.invoke) with built-in Enum.values / .byName(), exhaustive switch expressions, and standard package:test group() / test() blocks.
  • Code Generation: Use package:build, package:build_runner, and package:source_gen (or json_serializable, built_value, freezed) for serialization, dependency injection, and boilerplate generation.
  • Ahead-of-Time Reflection: For workloads requiring reflection-like runtime capabilities, use pre-generated reflection via package:reflectable and package:reflectable_builder.

Activity

  1. added
    area-metaCross-cutting, high-level issues (for tracking many other implementation issues, ...).
    on Dec 16, 2020
  2. daniel-v commented on Dec 16, 2020

    @daniel-v

    To make sure the info gets to you, here is my response to dart-misc mailing list:

    I might be reading too much into this but discontinuing dart:mirrors library has some serious consequences that were not mentioned in your mail. The most important is that we'd lose the ability to do runtime reflection at our server-side code. It is an HTTP server that uses mirrors for the most important bits of it. Transitioning away from it is a scary proposal for many reasons.

    If anything I'd request to add mirrors to AOT snapshots too, instead of discontinuing, so that we'd get to deploy our code as a single binary with all the benefits and drawbacks it means. But... Let me address the concerns I have:

    • There is no proposal as to what would replace it, we need more info what you had in mind.
    • If there is no replacement, you'd effectively eliminate some useful features from the language/runtime we actively use. I don't think that's the case; I will assume for the rest of the mail that you are thinking in terms of migration to code generation.
    • We enjoy the ability of quick iterations on BE code without having to run builders all the time, which adds a lot of compile time overhead.
    • dart:mirrors is easy to learn and test, package:build is most definitely not
    • I keep struggling with different build_runner versions on FE code where package:analyzer constraints prevent me from using certain packages together (eg. Angular 5.3 and freezed). This issue does not exist with runtime reflection.
    • Builders can be really slow, in some cases I'd prefer the runtime overhead instead of the added minutes of wait time for the builders to complete.

    I'm sure you have a list of pros and cons for mirrors vs. code generation too. Having said that, I'm not against the idea completely if there are good enough reasons.
    I'd have to bite my lips hard if mirrors were removed today and would have to migrate to pacakge:build but luckily we still have some time.

    There have been several precedents where such a change was preceded by preparing users, crafting new tools or giving some extra attention to alternatives. Most recent one is the NNBD tooling to this. I hope it is the case here as well, in which case feedback on why I'm nervous about moving to pacakge:build is warranted here. In my opinion, build and associated packages are in a bad spot right now.

    My recent incursions to writing builders were ... trying because:

    1. after awhile, I gave up trying to find written documentation for the questions I had, I started digging around in the code - this shows that the there is too little information out there about how to write builders, how it works and how to configure them. Eg. I was surprised to see that build.yaml and build.config.yaml are merged together instead of being taken as an absolute configuration BUT with a huge caveat: builder declarations are ignored from build.config.yaml.
    2. the configuration space is very complex and the tools do not aid me rooting out configurations that make no sense. ( ref ) Figuring out what is OK and what is not can only be done through trial and error or by reading the implementation. This is a problem for every user of package:build, not just builder authors.
    3. Contributors of package:build stated it is intended for "superusers" ( ref ). Context to it is: I brought up that it is difficult to map how I see code as a developer to how analyzer sees the same code, then write a builder around that mapping. dart:mirrors make it fairly easy. I fear that I would be required to run around in language specifications to figure out what is what for a question which as simple as: What is the first method in this class with an annotation of Route('/')? Example of how complicated it can be just to resolve types here (used by Angular Compiler too).

    I've had some steep learning curves over the years. package:build was one of them. The primary reason - which I consider the most important of all - is a conceptual one: Code generation is becoming more and more important piece of the language ecosystem. As such, as a user of the language, my expectation is that such an important piece is well-documented, somewhat fool proof and fairly easily approachable.

    I hope that this is the kind of feedback you were looking for to help you make the right decisions.

  3. mit-mit commented on Dec 16, 2020

    @mit-mit
    MemberAuthor

    To those downvoting, it would be great if you could also post a comment with what concerns you. As mentioned, we'd like to understand the use cases where you use mirrors today, so that we can investigate if there is an alternate way by which we can support those use cases without mirrors.

  4. zmeggyesi commented on Dec 16, 2020

    @zmeggyesi

    I work with @daniel-v and @gothamgasworks - I can second Daniel's input that removing runtime reflection would impact us quite negatively.

    Our product actively uses runtime reflection in server-side Dart code, both in production and in development, and our preference would be to retain the ability to introspect code rather than rely on build-time code generation, both in the interest of development cycle times and ease of understanding of deployed code.

  5. gothamgasworks commented on Dec 16, 2020

    @gothamgasworks

    @daniel-v 's summary is pretty good, I agree with them.

    Some things I want to emphasize:

    1. Quick iteration, because we don't need to wait for builds (we use kernel snapshots on the production server)
    2. No need to develop builder code for every use case that may arise (we encountered quite a couple)
    3. Builder code development is a drag, we have to constantly update the code compared to our mirrors code, which we only had to touch once (moving from Dart 1 to 2)
  6. gothamgasworks commented on Dec 16, 2020

    @gothamgasworks

    We use dart:mirrors in:

    • Database record class representation
    • Data merging tasks (like statistics, or updates coming from external systems)
    • Mapping server side business object to client side business objects
    • HTTP request routing
    • Serialization libraries (object serialization, object factory registration, XML serialization required by some external systems)
    • Report generation system

    Of course we could work around it with builders, I would just like to be able to not work with the builder/analyzer API, which requires a serious amount of maintenance as opposed to the mirrors API.

  7. isoos commented on Dec 16, 2020

    @isoos

    The use cases I had in the past month:

    • package:appengine uses reflection for entities that are stored in Datastore.
    • package:json_rpc_adapter uses reflection for both HTTP-client and HTTP-server JSON-RPC, by taking the abstract class Api { Future<Rs> operation(Rq rq); ...} like API definition and binding it to both.

    My wish-list, which would probably make the lack of dart:mirrors bearable:

    • Fast and simple-to-use source code parser library and good AST/API to extract the annotations, classes and methods that are of the interest of the tool (without specifying upfront which parts we are interested in). My problem with package:source_gen and related build-utilities is their complexity: they are two levels up the chain, they use the framework approach (having a single but somewhat complex way to do things), as opposed to a library that exposes the data and allows the tool to process it as they want. package:analyzer is not really without ceremonies either, but at least it is "only" a library. Sometimes I don't even need to explore the types recursively, I just need the field/methods's name the parameter names and their types. It is not an issue if the full source graph is not available right away, in many cases it may be a (performance) benefit.

    • A separate source code generator, that is not tied to any convention (e.g. not every project wants to use part for the generated files, nor do they necessarily want to place only a single file in the same directory as the source). I have done at least 4 separate code generators in the past years, and they had different requirements about these, and it was just easier to do String concatenation + custom file placement + calling dartfmt than to use any of the big frameworks. I want to build my source code in a programmatic way, the library can enforce the structure (class, method, function boundaries) and format it nicely, and that's all it needs to do. E.g. source_gen examples are full of generators, which assume that I want only to plug in a small piece somewhere. Instead, it should just take the tree as I build it.

    • A trigger mechanism which detects when the generator chain needs to run. I think the situation here is rather good, and I don't have much preference: package:build_runner's approach may be okay (if made simple for generic tool use), or even package:watcher can be used if the programmatic approach is preferred by the developer.

    • A more convenient way to live-reload classes in the Dart (JIT) VM. package:vm_service + package:watcher should be really a VM flag that can trigger a reload of classes locally, without further configuration or without adding dependencies to the application under development. (Yes, it may not cover 0.1% of the changes that may happen, e.g. depending on a new package, but restarting the local dev app every once in a while is not an issue. Restarting it every time is.)

    Summary: To replace dart:mirrors, the following tools need to be available:

    • parse and expose parts of the code efficiently
    • generate source code in a convenient way
    • (trigger source code generator when the inputs have changed)
    • reload the source code in the local dev VM without long ceremonies
  8. scheglov commented on Dec 16, 2020

    @scheglov
    Contributor

    The Dart Analyzer team uses package:test_reflective_loader extensively for all our tests, and it is based on using mirrors. We found out early that vanilla package:test is not structured enough for complicated package:analyzer tests.

  9. lexaknyazev commented on Dec 16, 2020

    @lexaknyazev
    Contributor

    My use case of reflection is docs generation via iterating over class members and invoking them to get sample outputs. The code using dart:mirrors resides in a grinder task thus being completely unaffected by the lack of support by web and AOT runtimes.

  10. jakemac53 commented on Dec 16, 2020

    @jakemac53
    Contributor

    I have mentioned this elsewhere but worth mentioning here for posterity - build_runner uses dart:mirrors in order to discover all of the libraries that exist in a program. In our case that is the build script itself, and we use that to know if we need to invalidate the snapshot.

  11. gmpassos commented on Dec 16, 2020

    @gmpassos
    Contributor

    I think that a new framework for reflection should exists. For now we should kill dart:mirrors.

    In the future, after NNBD, a new framework should exists with another name. One where you can mark classes that should have their full description after compilation (the main information needed for runtime reflection). The marked classes could be by annotation or passing a list of classes for the "refletor factory" that should be tracked, allowing any class to be tracked for reflection. With that approach,Dart tree shaking won't be affected, unless you really need a reflected class for your project.

  12. nex3 commented on Dec 16, 2020

    @nex3
    Member

    dart:mirrors is used by Grinder, which is the best (and only) task runner for Dart with an 84% popularity rating on pub.dev, and my the mustache package which is used by both Dartdoc and the 99%-popularity google_fonts package. Both of these are load-bearing ecosystem packages. Removing dart:mirrors without a clear plan for something similarly usable for these packages would cause a lot of downstream pain and chaos.

    It's worth noting that @isoos's proposed solution would likely not be sufficient for Grinder, since it's infeasible to have a code generation step as part of a task runner workflow.

  13. tvolkert commented on Dec 17, 2020

    @tvolkert
    Contributor

    This (a) doesn't solve all the problems with dart:mirrors, and (b) would only cater to specific use cases, but leaving the idea here nonetheless... what if we were to still tree-shake as normal even in the presence of dart:mirrors, and rely on developers marking methods as @pragma("vm:entry-point")?

  14. DavidLiedle commented on Dec 17, 2020

    @DavidLiedle

    My use case is... well, hold on just a dang minute.

    I don't think I should have to lay out my thinking process to express the fact that I value the presence of dart:mirrors in the language. This "what is your use case?" approach smells of management and budgeting issues; not of nurturing the Dart ecosystem with an optimistic eye on the 10-20 year future of the language.

    The APIs supported by mirrors are significantly behind the current Dart language, for example they do not support extension methods or null safety.

    So allocate the dev resources to get on that. "Significantly behind" is a prioritization issue.

    This is plainly technical debt. Defenestrating a sub-section of your server-side users building ORMs (oops, there's my use case..) would be an easy way out, and clearly a cheaper path forward. But seriously, cheaper over better? Not very Googley, imho. Do the right thing. Strive for excellence (and make the one thing you're doing really well be creating a language that can be used everywhere, including places where reflection makes sense). Keep an eye on the goals (both short and long term). Be proactive (and bring dart:mirrors up to par). Go the extra mile... Well, you know all this. You're the ones that work there.

    I've been one of the earliest, most enthusiastic, and staunchest allies of both Dart and Flutter, but the bloom is off the rose with several prioritization choices that make me just, well, nervous. I'm tired of building things in AngularDart, or Polymer.dart (oy..), and then getting downstream just enough to feel the aging, unfinished edges and have to admit to myself that it's not a viable path forward. I have had to abandon chunks of my work because of hitting these dead ends and faded photos of what was planned. It's turning my attention to other places, and mine has teh sad. It took this thread for me to even glance back at AngularDart and see that it received an update in October of this year. Yay! But.. I can't trust it; already tried that, and got left behind.

    TL;DR: this decision will impact the trust of your user base regarding your team's intentions in general; trust that is already flying on one engine. If we smell even a whiff of "Flutter doesn't need it so it's getting de-funded", even the features you do nurture will be untrusted for any other use case, and weaken your entire value proposition. Many of us, even your biggest fans, will simply walk. Not a rage-quit, but a pragmatic step out of what would appear to be a populism-driven-dev world and into other environments that focus on remaining well-rounded. Over time. For everyone.

    The End

  15. zmeggyesi commented on Dec 17, 2020

    @zmeggyesi

    @gmpassos

    For now we should kill dart:mirrors.

    Not without a replacement being widely available and adopted we shouldn't!

    Evidently, mirrors and reflection as a whole is widely used, in load-bearing packages as @nex3 points out, not to mention in the major task runner of the ecosystem. Killing mirrors without a replacement at this point pretty much breaks the ecosystem; killing it with a replacement that's not (near-)universally adopted does the same.

    If the team moves forward with deprecating and removing this, it should only be done after they provided a replacement framework/solution, and the majority of the ecosystem, or at least its core components, have transitioned to the new way of doing things.

  16. 79 remaining items

  17. francescovallone commented on Feb 23, 2024

    @francescovallone

    Good afternoon 👋!

    Sorry to bother, but I'm trying to understand a little bit more about on the situation of dart:mirrors.

    I understood from this issue that right now dart:mirrors shouldn't be used in any critical case. I'm really fond of using reflection both for backend and object serialization (dartson-like) but I'm a little bit concerned about the current state of the library since as stated by @mraleph there is not a way to get the shape of a record for example.

    Can you give a more detailed update on what plans dart has for this specific library for example if it will be discontinued in a more permanent way in the near future or if the team is trying to provide an alternative?

  18. jakemac53 commented on Feb 23, 2024

    @jakemac53
    Contributor

    I can't speak with authority, I do not control such things, but I believe it is unlikely it would be removed in a non-breaking Dart language release (so, Dart 4 at the earliest). But, it also is unlikely to get many improvements such as knowledge about record types. It has always been an experimental/unstable API and never graduated to full support. We strive mostly not to break the existing use cases, until such time as there is a viable replacement. This issue exists primarily as a way to track those use cases that exist, such that we can make decisions in the future about the support of this API, as well as the features needed to make any replacement a viable one.

  19. lrhn commented on Feb 26, 2024

    @lrhn
    Member

    I wouldn't say that dart:mirrors has always been experimental. In Dart 1, it was fully supported, also on the web.

    That was at a significant cost, though, which was why one was strongly discouraged from actually using it on the web, and if you did, there were annotations that allowed some tree-shaking to happen anyway. It still broke tree-shaking in general and required extra metadata, so if just one corner of your web app uses it, your deployment size could grow a lot.

    Rather than worry about that, it was discontinued on the web, and never supported in AoT, and because of that dart:mirrors has not had any further development since about Dart 2.0. That's when it became "effectively unsupported". New compilers didn't need to support it, new platforms (including Flutter) didn't.
    It's a feature that only works on the JIT compiled VM, and only really with features that existed in Dart 2.0. That means you can use it for scripts (if you don't AoT compile them), and not much else.

    Using reflection in tests means that those tests cannot be run AoT compiled, which means they are not testing the code that's actually going to run in production. (Can't do dart test -c exe.)

    You can use dart:mirrors today, but you shouldn't.
    They only work in stand-alone programs that you can control how gets run.
    They should never leak into other people's code, or be run by any system that migth one day want to compile the code to exe first.

    I do hope that macros can give users what they need to replace existing uses of reflcection. Like a reflectiveTest that finds all methods with a name starting with test, a macro could find the same methods, and create an allTests() method that calls them one by one.

  20. sigurdm commented on Feb 26, 2024

    @sigurdm
    Contributor

    In Dart 1, it was fully supported, also on the web.

    https://codereview.chromium.org//61793006 marked it as "unstable" - that was 2013

    The documentation (today) says: This library is only supported by the Dart VM and only available on some platforms.

  21. lrhn commented on Feb 26, 2024

    @lrhn
    Member

    True, I guess the API was never considered stable, which makes sense if it has to match an evolving language.
    The phrasing there was "API might change slightly as a result of user feedback", which is far from "this doesn't work". And the comment should have been removed if such changes didn't appear soon after.

    The API at the time was supported everywhere.
    And the API has been stable, so the comment didn't end up meaning much.

    Mirrors, as such, were not considered experimental in Dart 1. The concrete API was, possibly, conisdered a little experimental, but after surviving a few releases, that disclaimer didn't mean much.

  22. jakemac53 commented on Feb 26, 2024

    @jakemac53
    Contributor

    Like a reflectiveTest that finds all methods with a name starting with test, a macro could find the same methods, and create an allTests() method that calls them one by one.

    Right, macros can definitely enable these class based test runners. It could pretty easily even just be a small layer on top of package:test, since tests/groups can be added programatically.

  23. gmpassos commented on Feb 26, 2024

    @gmpassos
    Contributor

    Macros will need to be able to inform the annotations of classes and methods or use them as filters and input parameters for generators.

  24. lrhn commented on Aug 20, 2024

    @lrhn
    Member

    Automatically generating code is what macros is intended to answer.
    A toJson macro is the first macro being built for the the feature, so it should be able to support that.

    Dart is not JavaScript, not everything can be converted to JSON, or a map. In some cases, choosing a good representation is non-trivial, and a naive conversion could be disasterous for performance. One size will not fit all.
    Building one design into the compiler risks that design not being what people want, and what people want may change over time. A feature needs to be either so complete and configurable that you can use it for everything, or it needs to be optional, so it doesn't matter if it becomes obsolete.

  25. bradcypert commented on Jan 30, 2025

    @bradcypert

    With the macros project being suspended, whats the plan for Reflection? I still dont find build runner sufficient (and its a pain to use imo).

  26. changed the title [-]Request for feedback: Discontinuing support for dart:mirrors[/-] [+]INTENT TO DEPRECATE: dart:mirrors[/+] on Sep 9, 2026
  27. changed the title [-]INTENT TO DEPRECATE: dart:mirrors[/-] [+]dart:mirrors deprecation and discontinuation[/+] on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-metaCross-cutting, high-level issues (for tracking many other implementation issues, ...).

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions