-
Notifications
You must be signed in to change notification settings - Fork 7
Repo Guidelines and Goals
This document outlines the standards and practices for maintaining and evolving our codebase. It serves as a guide for developers to ensure consistency, quality, and scalability. Specific sections below detail key areas of the codebase and our approach to maintaining them.
- Code Quality: All new code and changes should adhere to high standards of readability, maintainability, and performance.
- Incremental Improvements: We aim to improve the codebase with every change, avoiding technical debt where possible.
- Documentation: Update this README and relevant inline comments when introducing new patterns or modifying existing ones.
We are actively upgrading the codebase to use TypeScript with strict mode enabled. This is a gradual process to ensure stability and correctness.
- Type Annotations: Add explicit types to every new change or refactor. This includes function parameters, return types, variables, and state objects.
- Enable strictNullChecks: This rule is currently disabled. It's a critical rule that will be necessary for us to enable in order to ensure proper typing across the codebase. Once this is enabled and the issues have been resolved, we can move onto the next step.
- ESLint Integration: Once sufficient type coverage is achieved, we will enable ESLint rules to catch missing types and other issues. Regularly check for and resolve these warnings.
-
Strict Mode Activation: After type coverage is near-complete, enable TypeScript’s strict mode (
strict: trueintsconfig.json). Perform a final sweep to address any remaining issues (e.g., implicitany, null/undefined checks). - Approach: Prioritize high-impact areas (e.g., state management, API integrations) and incrementally refactor legacy code as part of ongoing work.
Goal: Achieve a fully typed codebase with strict mode enabled, ensuring robust type safety and fewer runtime errors.
We use the matrix-js-sdk for real-time communication and collaboration features. Matrix is the source of truth for all state related to channels, rooms, messages, and members.
-
State Management:
- Matrix maintains the canonical state internally.
- Redux is a mirror for UI display purposes only. Store the minimal state necessary to render the UI efficiently.
- Avoid duplicating or independently managing Matrix state in Redux.
-
MatrixAdapter Class:
- All transformations from Matrix state to Redux state must be encapsulated in a
MatrixAdapterclass. - This class should handle mapping, filtering, or formatting Matrix data into a Redux-compatible structure.
- All transformations from Matrix state to Redux state must be encapsulated in a
-
Guidelines:
- Minimize direct Redux state mutations; prefer syncing data from Matrix as it maintains the correct state internally.
- Ensure UI updates reflect Matrix state changes promptly and accurately.
Goal: Maintain a single source of truth in Matrix, with Redux serving as a lightweight, reactive layer for the UI.
Effective logging is critical for debugging and monitoring, but it must be controlled and purposeful.
-
Avoid Console Logs: Discourage use of
console.logfor debugging. Remove them before merging code. -
Sentry Integration:
- Leverage Sentry for comprehensive error tracking.
- Capture all unhandled exceptions and key handled errors with sufficient context (e.g., user ID, action, stack trace).
- Regularly review Sentry reports to identify and prioritize bug fixes.
-
Best Practices:
- Log meaningful events (e.g., API failures, state mismatches) rather than excessive debug noise.
- Ensure sensitive data (e.g., PII) is masked or excluded from logs.
Goal: Provide actionable insights into application behavior without cluttering the codebase or exposing sensitive information.
Components should be lightweight, focused, and closely tied to the UI they render.
-
Legacy changes:
- All new components should be function components that utilize hooks.
- "Container" components that are wired up to redux should be aggressively refactored to function components to optimize re-renders.
-
State Access:
- Use Redux hooks (
useSelector,useDispatch) to read and update state. - Read state as close as possible to where it’s displayed to minimize prop drilling and improve traceability.
- Use Redux hooks (
-
State Transformations:
- Avoid destructive operations (e.g., sorting, filtering, overriding properties) in higher-level components.
- Perform transformations right before rendering in the component that displays the data. This ensures lower components re-render correctly when state changes.
-
Guidelines:
- Keep components small and single-purpose.
- Avoid complex logic in components; delegate to utilities, adapters, or Redux thunks/sagas.
Goal: Build predictable, maintainable components that reflect Redux state accurately and efficiently.
Files should be named and organized in a way that makes the code base easy to follow and quick to search. The name of the file should clearly state what is inside of it, this will help us keep the files clean and organized. There is little friction to adding multiple components or functions to a file called index because it already doesn't clarify what is inside of the file.
-
Legacy changes:
-
indexfile name should be reserved for organizing directory exports. - For example:
Storing application code inside of// messenger/list/index.ts export { ConversationItem } from './conversation-item/conversation-item'; export { ConversationListPanel } from './conversation-list-panel/conversation-list-panel'; export { MessengerList } from './messenger-list'; // conversations-sidekick.tsx import { MessengerList, ConversationItem, etc. } from './messenger/list';
indexfiles makes it hard to find and inevitably ends up in the file having more than one responsibility. -
Abstractions should be deliberate and driven by real use cases, not premature optimization.
- Duplication is OK: Duplicating code is acceptable initially. It allows flexibility and faster iteration.
-
When to Abstract:
- Create abstractions only after identifying multiple concrete use cases (e.g., 2-3 instances of similar logic).
- Use these examples to infer a reusable, well-fitting abstraction.
-
Avoid Over-Engineering:
- Don’t build abstractions for hypothetical future needs. This risks creating rigid, overly complex solutions that may not fit actual requirements.
-
Feedback Loop:
- When adding new code, evaluate if it aligns with existing patterns that could benefit from abstraction.
- Refactor incrementally as patterns emerge naturally.
Goal: Foster a codebase that evolves organically, with abstractions that solve real, repeated problems effectively.
- Testing Standards (e.g., unit tests, integration tests)
- API Integration Guidelines
- Performance Optimization
Feel free to expand this README as new standards or components emerge. Contributions and suggestions are welcome!