Do and Do Not

A definition is the contract between your software and its integrators, and contracts age. This checklist collects the design habits that keep a definition usable, evolvable, and honest, and the shortcuts that come back as breaking changes.

Do

  • Keep entity names and property names stable once published. Property names are what every configuration file spells out, and entity identities are what references and cross-file merges bind to. A rename is a breaking change for all of them, plus for the generated code.

  • Set default explicitly whenever fallback behavior matters. A default in the definition is visible in hover, in the merged export, and in the generated accessors. A fallback hidden in the consuming code is invisible to the configuration author and to every other consumer.

  • Constrain externally provided values. min/max, pattern, and length bounds turn integration errors into edit-time diagnostics. An unconstrained u16 register offset or an unbounded string is a validation you pushed onto the ECU at runtime.

  • Write title and description for every user-facing element. They are the API documentation the configuration author actually reads, in hover and completion. Descriptions support Markdown.

  • Keep a definition focused. One definition per functional domain; split large models rather than growing a monolith. Shared building blocks move to types and, when used across definitions, get scope: "public".

  • Use unit for physical values and keep the property name unit-neutral. sampleTime with unit: "ms" can become microseconds in version 2; sampleTimeMs cannot.

  • Choose multiplicities deliberately and document list expectations. 0.. and 1.. read the same in a half-filled configuration; only the diagnostic differs. Say in the description when an empty list is meaningful and when it is not.

  • Anchor patterns for identifier, path, and other format-bound strings. Patterns are unanchored by default, and ^…​$ is almost always what you mean.

Do Not

  • Do not overuse 1..1. Every required field is work for every integrator, forever. If a safe default exists, the field is optional, see the multiplicity guidance in the definition system.

  • Do not share types across definitions casually. scope: "public" marks a type as API for other definitions; everything without it stays definition-local and free to change. Cross-definition use without the explicit scope does not resolve, and that is the point.

  • Do not declare broad refTargetTypes. A reference that may point at half the model is hard to complete, hard to validate, and hard to consume. Name the exact entity types you expect.

  • Do not change enum literals or tighten constraints without versioning. Removing a literal, shrinking a range, tightening a pattern, or adding or tightening an Error constraint invalidates existing configurations. A Warning or Info constraint needs no new version, raising it to Error does. Bump the major of definitionVersion (the minor for 0.x) and plan the migration. The version recorded in each configuration’s $definition is how the drift becomes visible.

  • Do not encode physical units in property names. That is what unit is for, see above.

  • Do not use null for absent values. Confµgami has no null type. An optional field without a value is omitted, in the configuration and in the generated bindings.

  • Do not rely on implicitly exported defaults for runtime behavior. If the software depends on a fallback, declare it as a default in the definition, where it is validated and versioned, instead of assuming what a generated schema or binding happens to fill in.

  • Do not write vacuous patterns. A pattern: ".*" documents an intention without enforcing anything. Either state the real format or drop the pattern and rely on the length bounds.