A bug shows up in a Flutter debug session after several hot reloads but not after a hot restart; how do you tell a reload artefact from a real bug?
answer
- new code, old state
- kernel files into the VM
- no code re-executed until rebuild
- rejected reload leaves old code
- reproduce from a cold start
basics
~20 sHot reload runs new code on state created by old code, so objects, statics and initState setup can be impossible combinations. Hot restart and reproduce from launch: if the bug is gone, it was a reload artefact; if it persists, it is real.
solid answer
~40 sA hot reload recompiles the changed libraries to kernel files, the VM reloads them, and then only the widget rebuild runs; `State` objects, singletons, statics and anything set up in `initState` or `main()` keep values produced by earlier code. After several reloads the app can be in a state no user could reach. So first hot restart and follow the user's path from launch. If the bug is gone, it was an artefact of stale state or a silently rejected reload; check the console for "Hot reload was rejected". If it survives a hot restart, it is real, and if native code is involved, confirm with a full restart. Always verify fixes from a cold start.
go deeper
Remember to hot restart before chasing a strange bug that appeared after reloading.
Explain the reload steps, recompiled libraries, kernel files, VM reload, then rebuild, and why only build code re-executes.
Show a routine for separating artefacts from real bugs, including rejected reloads, unflagged final globals and native changes, and verifying fixes from a cold start.
Build team norms, such as cold-start verification before review and reload-friendly conventions, so reload artefacts never turn into filed bugs.
## What a hot reload actually does Understanding the mechanism is what lets you tell a real bug from a reload artefact. When you trigger a hot reload in a Flutter debug session, the Flutter docs describe these steps: 1. **Recompile only what is needed.** The host looks at the code edited since the last compilation and recompiles: every library with changed code, the application's main library, and the libraries on the path from the main library to the changed ones. 2. **Ship kernel files.** Those libraries are compiled into kernel files and sent to the Dart VM on the device. 3. **Reload libraries in the VM.** The VM reloads all libraries from the new kernel file. At this point **no code has been re-executed**. 4. **Rebuild.** The framework triggers a rebuild, re-layout and repaint of all existing widgets and render objects, so `build` methods run with the new code. Everything not re-executed by step 4 keeps running on the old data: the `State` objects, the objects they hold, singletons, and every global and static field. ## Why post-reload behaviour can differ from a fresh start The docs call this **"previous state is combined with new code"**. After several reloads, the app is running the latest code on data produced by **earlier** versions of that code. Typical artefacts: - **Stale derived data.** An object built by the old code (a cached list, a parsed configuration, a controller configured in `initState`) still has the old shape or values. - **Stale globals and statics.** A `static final` or top-level `final` still holds the value from its first evaluation. Most such initializer changes are flagged by the VM as needing a hot restart, but the docs show that a `final` global derived from a `const`, such as `final bar = foo;`, is not flagged. - **Missing side effects.** A listener added in `initState` or `main()` with new logic was never registered; the old one still runs. - **Rejected changes.** If an enum became a class or a generic parameter was added, the reload was rejected and the app still runs the old code, even though the editor shows the new code. - **Framework-specific exceptions.** The docs note that changes to a `CupertinoTabView`'s `builder` are not applied by hot reload. So a bug that appears after reloading may be a real bug or an impossible state that no user could ever reach. ## A diagnosis routine 1. **Hot restart and reproduce.** If the bug disappears after a hot restart and does not return when you follow the user's path from launch, it was a reload artefact. 2. **If it survives a hot restart**, it is real Dart behaviour. If it involves native code, confirm with a full restart as well, because a hot restart does not rebuild the host. 3. **Check the console** for a rejected reload (`Hot reload was rejected:` with the hint to hot restart). Continuing after a rejection means testing old code. 4. **Look for state that outlives rebuilds**: statics, singletons, `initState` setup, providers or services created at start-up. 5. **Before a bug report or a pull request**, always verify from a cold start. A fix validated only by reloading may depend on state the fix never creates. ## Making code reload-friendly without bending it | Habit | Effect on hot reload | |---|---| | Constants as `const`, derived values as getters | Edits show immediately | | Pure computation in `build` or small helper functions | Reruns on every reload | | Start-up wiring kept small and explicit | Fewer edits that need a restart | | Expensive work still in `initState` or services | Correct; restart when you change it | Do not move controllers or subscriptions into `build` just to see edits; that trades a dev-loop convenience for real bugs. ## Communicating it In an interview, the senior signal is naming the mechanism, **new code, old state**, and showing a routine: reproduce from a clean start before investing time. It is the same habit that keeps a team from filing bugs that only exist in one developer's reloaded session.
- The editor shows your new enum-based model, but the app behaves like the old code. What happened?The reload was probably rejected, for example because an `enum` became a class, so the VM kept the old libraries. The console shows "Hot reload was rejected" with the hint to hot restart. Until you do, you are testing the old code.
- Which Dart global does the VM fail to flag when its source value changes, according to the Flutter docs?A `final` global derived from a `const`, such as `final bar = foo;` after changing `const foo`. `foo` updates on reload, but `bar` keeps its first value and the VM does not flag it. Declaring `bar` as `const` or a getter fixes it.
saying these in an interview costs you the question
- If hot reload succeeds, the app state is identical to a fresh launch.
- Hot reload re-executes all top-level code in changed libraries.
- A bug that survives hot restart must be a native bug.
- Continuing to test after a rejected reload runs the new code.
- Moving controllers into build is a good way to make edits reloadable.