- When implementing changes in this codebase, prioritize consistency and consolidation over novelty.
- Default to no comments. Code, names, and types are the documentation. Add a comment only when a future reader cannot recover the intent from the code itself.
- Never restate what the code does. If the comment paraphrases the next line, delete it.
- Never explain a change you are making. Comments are about the resulting code, not about the diff or the review that produced it.
- Never narrate trivial mechanics ("seed the form", "fall back to default", "guard for unmount", "cleanup on success", "preserves siblings", etc.). If the function/variable name doesn't already say that, rename instead of commenting.
- No "why" boilerplate. Don't write comments like "we do X so that Y" unless Y is genuinely surprising and not visible from reading the function. A two-word identifier (
safeFallback,corsHeaders, …) usually beats a three-line comment. - Allowed comments (rare, deliberate):
- Workarounds for third-party bugs, with a link or version reference.
- Non-obvious invariants or ordering constraints that aren't enforced by the type system.
- Explanations required by another rule in this file (e.g. the
asrule, the MUI polymorphic-strip cast rule). - Spec citations when the code implements a non-trivial part of an external spec.
- JSDoc on exported APIs is fine when it documents the contract (parameters, return shape, edge cases) — not when it restates the implementation.
- Delete stale comments aggressively. A comment that lies is worse than no comment.
- Always look for existing components, hooks, utilities, routes, and data-access patterns before adding new ones.
- Extend or compose existing abstractions when possible instead of introducing parallel implementations.
- If a near match exists, adapt it to support the new use case rather than duplicating logic.
- Avoid using TypeScript's
astype assertions unless absolutely necessary. - Only use
aswhen there is no safer or more idiomatic alternative (for example, when interfacing with third-party or legacy data you cannot control). - When you need to use
as, always add a code comment explaining why it is required in that context. - Prefer type guards, runtime validation, and stricter data structures to ensure type safety and clarity instead of using type assertions.
- Rationale: Overuse of
ascan hide bugs, undermine type safety, and reduce code maintainability and refactorability.
- New features should follow established naming, folder structure, API shape, and UI conventions already used in neighboring code.
- Keep behavior and architecture aligned with existing patterns unless there is a clear technical reason not to.
- If divergence is necessary, keep it minimal and document the reason in the PR/commit notes.
- Prefer using existing mutation/query status and returned data to drive UI state before introducing extra local component state. When a hook already exposes the success, error, pending, or data needed for rendering or navigation, derive from that first.
- During feature work, opportunistically consolidate duplicated code paths into existing shared patterns where safe.
- Prefer small, incremental refactors that reduce pattern drift without broad unrelated rewrites.
- Do not introduce new abstractions unless they are reused or clearly expected to be reused.
- No user-facing content strings (error messages, labels, placeholders, etc.) should live in the shared package.
- Keep content strings colocated in the consuming app (e.g. web) so they can be localized and adjusted per product without touching shared code.
- The shared package may define error codes, types, and structure; the app defines the human-readable messages.
- Use
npmas the default package manager for this repository. - Prefer
npmcommands overpnpmoryarnunless the user explicitly asks otherwise. - When installing dependencies, running scripts, or reproducing CI/local workflows, follow the root
package-lock.jsonas the source of truth.
- For Synology integrations, treat the public DSM Login Web API guide and File Station API guide as generic protocol references only.
- Do not assume the File Station docs define the contract for
SYNO.SynologyDrive.*endpoints. - The source of truth for Synology Drive API availability is the target NAS via
SYNO.API.Info. - When typing
SYNO.SynologyDrive.*responses, prefer shapes validated against live NAS responses over guessed or loosely related public documentation.
- Always pass a named function to
memo()instead of an anonymous arrow function (e.g.memo(function MyComponent() { ... })notmemo(() => { ... })). - This ensures memoized components are identifiable in React DevTools and error stack traces without needing a separate
displayNameassignment.
- For non-trivial styling, prefer extracting a named
styled(Component)declaration over inliningsx={{ ... }}in JSX. Reservesxfor one-off, dynamic, or prop-driven styles where extracting would obscure intent. - Always include the underlying component's name as a suffix in the styled component's name so the underlying element is obvious at a glance: e.g.
styled(DialogContent)→HeaderDialogContent(notHeaderContent),styled(Stack)→ActionsStack(notActionsRow),styled(Typography)→WarningTypography. - Place styled declarations near the top of the file (after imports) so they're easy to scan.
- When
styled(Component)strips a polymorphic prop (most commonlyTypography'scomponent), cast the result back totypeof Componentand add a one-line comment explaining the cast — see theasrule above. - Rationale: named styled components show up as themselves in React DevTools, eliminate repeated inline blocks, and let readers understand the rendered DOM without crawling through
sxprops.
- Never use
React.FC,FC, orFunctionComponentto type components. Treat that pattern as deprecated and do not add it in new code. - Prefer explicit props types on the function instead, e.g.
function MyComponent(props: MyProps) { ... }orfunction MyComponent({ a, b }: MyProps) { ... }, and rely on inferred return types (or annotate the return only when it genuinely helps). - Rationale:
FCis discouraged by current React and TypeScript practice (including implicitchildren, weaker inference, and unnecessary indirection).
- Default choice: the solution that best matches current repository patterns and reduces long-term fragmentation.