Then the call to #state{connected=false} should be invalid to Dialyzer because it implies #state{connected=false, mode=undefined}. By having no defaul.t values for elements that are to be dynamically configured and making sure you initialize things in the init/1 callback, times where you forget to pass in the original variable will detect sending in an uninitialized state.
You need a single field to make this work, and mandatory fields on maps should also be working the same (#{mandatory := type(), optional => type()}).
That’s incredibly helpful, thank you very much!
On top of the error checking, defining types for the record fields also servers a valuable documentation purpose.
I do use dialyzer from time to time on my codebase (it is some kind of a pet project at the moment, so there is no professional CI implemented), but I deserve another slap for plastering the dialyzer settings in rebar.config with 11 occurrences of no_<warning>…
One minor drawback of leaving out defaults and specifying types is that in this case the record must be created in a single step, not incrementally - otherwise Dialyzer will complain. For huge records it’s sometimes annoying, so I ended up adding undefined for some types. Still, it’s better than nothing.