Originally, there was a default: option in node.def files for storing default values. Its aim was to make it simple to set default values for leaf nodes, and to abstract them away.
It abstracted them away deep in libvyattacfg—by silently returning the default value from calls to return_value() etc. if a node value is not set in the config.
That approach was proven problematic. Logically, a node exists in the config or it does not. If it does not exist, exists() should return false and return_value() should fail. If it does exist, then exists() returns true and return_value() returns its value. The old approach to defaults created a third virtual "default" state when a node doesn't exist, but has a value.
Apart from breaking the logic, it also made it difficult to produce a reverse diff for reverting changes, which made rollback without reboot impossible.
Does it mean defaults encoded in command definitions is a bad idea? Not quite. There's a better approach where config scripts can query the default from those definitions instead of hardcoding it in the script itself, but existence of a default has no effect on the way exists() and return_value() behave.
For that we can add a new <defaultValue> tag under <leafNode> and then generate default dicts from those definitions.
However, the convertor must not produce default: option in the node.def's it makes!