Contributing¶
The conventions every change follows, to keep the codebase consistent, tested, and portable across the desktop, browser, and mobile heads.
Coding conventions¶
- No static methods or properties — except Avalonia
AvaloniaProperty.Registerand framework metadata. Prefer instance members so everything stays injectable and testable. - Localization is resx-only. All translatable strings live in
Strings.en/de.resx(or a domain-specific resx pair) and are referenced throughLocalizationService. Both languages must carry every key. See Localization. - No empty catch blocks. Log through the app logger and surface user-facing failures through the dialog service.
- A new
FieldDefinitionsubtype changes nothing outside its own files — dispatch is virtual and registration is by type name, so there are no type-switches to update. See Adding a field type. - Missing a field type? Ship a simple version plus an on-screen note rather than silently skipping the use case.
- No trademarked words in source files.
- Keep dependencies conservative — official Microsoft or well-regarded community packages only, and prefer the built-in BCL over niche third-party libraries.
- Credentials are handled carefully. Passwords use PBKDF2-HMAC-SHA512 with a per-user random salt, and the iteration count and algorithm are stored alongside the hash. Nothing is ever kept in plaintext or in any reversible form. See Accounts.
Tests are required¶
Every behaviour change — features and bug fixes alike — is developed test-first, and ships with all three test layers: unit, integration, and headless. Line coverage and the mutation score must not drop. The Testing page walks through the workflow in detail.
Avalonia gotchas worth knowing¶
- Dynamic
MenuItemsubmenus must be built in code-behind; XAMLItemsSourcebinding doesn't render submenus in this Avalonia version. IsVisibleon a null sub-path evaluatestruewhen the object is null — addFallbackValue=False.- Never replace an
ObservableCollectioninstance — mutate it in place (Clear()+Add()). - Compiled bindings are on by default — every
DataTemplateneedsx:DataType.
Pull requests and release notes¶
Every change ships as a pull request — never a direct commit to master. Two conventions make the
release notes write themselves:
- The PR title is the release note. Write it as a single, user-facing sentence describing the change the way it should read in the release notes, not as an internal summary. The release is built from these titles, so "Collapse the search filters into a Filters toggle on narrow windows" is right, while "refactor SearchBarViewModel" is not.
- Label the PR so it lands in the right section. Release notes are grouped by label via
.github/release.yml:
| Label | Section |
|---|---|
feature |
Features |
fix or bug |
Fixes |
A PR with none of these labels does not appear in the release notes at all — that's how deployment, CI, chore, and docs-only changes are kept out. The flip side: if you forget to label a real feature or fix, it is silently dropped from the notes, so add the label when you open the PR.
Before you open a change¶
- Make sure all three test layers are green and the coverage and mutation gates still pass.
- For UI changes, verify manually in the running app with explicit repro steps — see Building.
- Give the PR a release-note-quality title and the right
feature/fixlabel (or leave it unlabeled if it should stay out of the notes).