Coding standards
These rules apply to first-party source, tests and tools. AGENTS.md owns architecture, asset safety and workflow; this file owns code authoring. .editorconfig owns formatter and IDE diagnostics. Package caches, third-party libraries and generated source are excluded from style edits; change a generator rather than patching its output.
C# and Unity
- Use C# 9 with block namespaces, explicit access modifiers and Allman braces. No file-scoped namespaces, global usings,
goto or regions.
- Use tabs with a width of four. Keep one logical statement on one physical line within 180 columns. Name intermediate operations instead of wrapping calls, conditions or expressions. Brace-delimited initializers, block lambdas and signatures exceeding the limit may span lines.
- Types, methods, properties, events and constants use
PascalCase; interfaces start with I; private fields, including static fields, use _camelCase; locals and parameters use camelCase.
- Prefer
public and private. Use internal for an intentional assembly boundary or test seam and protected for an inheritance contract.
- Declare interfaces before implementations in the same file. Inside a type, order non-private nested types, constants, static fields, serialized fields, other fields, properties, constructors, Unity callbacks, methods, then private nested types. Never reorder serialized fields merely for style.
- Order methods by access: public static, public override, public abstract, public, internal, protected, private. Keep each access group contiguous. Unity callbacks precede those groups regardless of access.
- Keep constants in the existing constants owner and tunable designer values in config assets. Use
nameof when a name comes from an identifier. Do not rename saved fields, enum values or asset bindings for style.
- Prefer guard clauses and remove dead or speculative fields, callbacks and abstractions. Keep state only when it must survive between calls.
- Resolve dependencies at composition boundaries. Extend the template’s existing services and package APIs before introducing abstractions. Do not retain an installer or introduce new global locators, singletons or static events.
- Prefer UniTask for Unity asynchronous operations. Propagate the owning lifetime’s cancellation and observe failures. Avoid
async void outside engine callbacks.
- Give subscriptions, loaded assets, input activation and services one owner with idempotent teardown. Do not mutate authored configuration assets at runtime.
- New Editor interfaces use UI Toolkit in Editor-only assemblies. Maintain existing inspectors without unrelated rewrites. Runtime assemblies must not import
UnityEditor.
- Document non-private types, methods and properties with short XML summaries. Types and methods use multi-line summary blocks; properties use one line when it fits. Put documentation above the complete attribute run.
- Do not document private members, constructors, tests, generated declarations or presenter data payloads. Fields and events need documentation only when their contract adds useful information.
- Use
/// <inheritdoc /> for implementations and overrides of documented contracts. Do not inherit documentation from an undocumented member. Avoid parameter, return and exception tags unless the contract needs them.
- Default to no implementation comment. Keep a single line only for durable rationale that names and code cannot express: an invariant, ordering constraint or external workaround. Never narrate obvious behavior, edit history or an issue.
- Summaries describe the consumer contract; they do not explain private call graphs. Keep comments and documentation accurate when behavior changes.
Tests
- Name tests
Behavior_Condition_ExpectedResult. Assert public behavior and costly silent invariants; avoid private reflection, designer-value assertions and redundant tests that mirror implementation.
- Use independent legacy fixtures for persistence compatibility. Preserve exact PlayerPrefs and engine settings before tests change them, and restore them after the test-owned application has torn down.
- Keep tests in their existing EditMode and PlayMode assemblies. Use the nearest test guidance for engine-specific constraints.
- Python uses the standard library, four-space indentation and explicit failures. Keep commands executable from the repository root and outputs under ignored
Artifacts/ directories.
- Shell targets bash 3.2 and BSD userland. Quote paths and globs, preserve exit codes, and avoid GNU-only assumptions. Keep independent Unity jobs serialized.
- Prefer native Unity CLI and Build Profiles over project-specific wrappers around engine APIs. Use a custom Editor helper only for a requirement those mechanisms cannot meet.
- CI actions use full commit pins and minimum permissions. Keep package references public, portable and locked to published releases.
Verification
Tools/validate.sh static checks repository invariants and source formatting. .editorconfig supplies naming and accessibility diagnostics in supporting IDEs; the Unity compiler does not enforce those IDE diagnostics.
- Review documentation scope, member order, comment value and dependency ownership explicitly. Formatting checks cannot prove these semantic rules.
- Run compile, EditMode and PlayMode checks after C# changes. Use player builds and visible checks when runtime or rendering behavior changes. Report each evidence layer separately.