This document provides a comprehensive overview of the development process for the HRM project, from initial setup to deployment. It covers our core principles, development workflows, and architectural standards. For a detailed list of enforceable standards and configurations, please refer to the Development Standards document.
For a fully automated setup, please refer to the "One-Click Start with DevContainer" instructions in the main README.md.
For manual setup, the project includes a script to ensure a consistent environment:
./scripts/setup.shThis script will:
- Create a
.env.localfile from the example if one doesn't exist. - Install all dependencies using
pnpm.
⚠️ Important: This project usespnpmas its package manager. Do not usenpm install, as this will create apackage-lock.jsonfile, causing conflicts with the officialpnpm-lock.yaml. The pre-commit hooks will block any commits that include this file.
For detailed information on specific topics, please refer to the following documents:
- TypeScript Best Practices
- Environment Variable Handling
- Testing Guidelines
- Hook Design Patterns
- Code Review Guidelines
- Quality & Security First: We prioritize writing high-quality, secure code. All contributions are subject to a thorough review process.
- Consistency: A consistent codebase is easier to understand and maintain. We adhere to established patterns and style guides.
- Continuous Improvement: We are always looking for ways to improve our code, processes, and tools.
To ensure a smooth and efficient review process, every PR must be tightly scoped.
- One Logical Change Per PR: Each PR should address a single concern (e.g., one bug fix, one feature).
- Separate Refactoring: Architectural changes and large-scale refactoring should be done in separate PRs from feature work or bug fixes.
- Focused Fixes: Critical patches or hotfixes must be tightly focused on the issue at hand, without any unrelated changes.
- Clear, single purpose: The title and description of the PR should clearly state the purpose of the change.
- Relevant changes: All code changes should be directly related to the stated objective.
- No unrelated cleanup: Avoid including unrelated cleanup or refactoring in the same PR.
- Accurate description: Ensure the title and description match the actual code changes.
- Focused testing: Tests should cover the specific scope of the change.
- Ensure changes are backward compatible or that any breaking changes are clearly documented.
- Add or update tests to cover new or modified functionality.
- Update documentation if the changes affect project usage or architecture.
- Create or update an ADR for significant architectural decisions.
To ensure consistency across the project, we use a standardized set of GitHub labels. These labels are used for issue triage, PR categorization, and automated workflows.
All managed labels are defined in .github/pr-labels.json. Automated workflows ensure these labels exist in the repository.
Key Label Categories (Strictly Limited to 5 per Category):
- Review Status:
ai-reviewed,approved,changes-requested - Change Type:
bug,enhancement,refactor,chore,documentation - Scope:
scope:focused,scope:needs-review - Priority:
priority:high,priority:medium,priority:low - Status:
needs-info,invalid,stale,duplicate,wontfix - Automation:
automerge,auto-code,bot-generated,triage-needed
Obsolete or redundant labels (e.g., variations like CI or workflow) are automatically removed by the cleanup-pr-labels.sh script, as configured in .github/automated-labels.json.
The security and quality review process is a critical step in our development lifecycle. It helps us catch potential issues before they make it into production.
- Standardized Review Format: We use a standardized format for security and quality summaries to ensure consistency and thoroughness.
- File-by-File Audit: A file-by-file audit approach ensures that every change is carefully examined.
- Iterative Feedback: We encourage multiple review cycles with iterative feedback to achieve the best possible outcome.
To maintain code quality and consistency, this project uses a combination of automated tooling.
This project uses Husky and lint-staged to automate code quality checks before each commit. When you run git commit, the following actions are automatically performed on the files you've staged:
- Prettier: Your code is automatically formatted to ensure a consistent style across the entire codebase.
- ESLint: The linter runs to catch potential bugs, enforce best practices, and fix any auto-fixable issues.
If ESLint finds errors it cannot fix automatically, the commit will be aborted. You must fix the reported errors before you can successfully commit your changes.
This automated process ensures that code merged into the leader branch always adheres to our quality standards without requiring manual checks. You can run these checks for the entire project at any time with pnpm run lint and pnpm run format.
To ensure a clean, readable, and automated changelog, this project enforces the Conventional Commits specification. For detailed configuration and rules, see the Development Standards.
Our CI/CD pipeline automates many aspects of the development process, including linting, testing, and deployment. We also have several automated workflows to help with tasks such as squashing and rebasing PRs, resolving conflicts, and analyzing technical debt.
The project is deployed to a self-hosted production environment using a GitHub Actions workflow. The deployment follows a "hard restart" strategy.
For a comprehensive guide on testing, including our testing structure, commands, and best practices, please refer to the Testing Guide.
Over time, your development environment may accumulate build artifacts, package manager caches, and other temporary files that can consume significant disk space. The project provides a set of scripts to manage this, offering different levels of cleaning depending on your needs.
To remove only the files generated within this project (such as build artifacts, logs, and test results), run the following command:
pnpm run cleanThis is the safest and most common cleanup task you will need. It removes:
.next/,dist/,coverage/,playwright-report/directories- Root-level TypeScript build info files (
*.tsbuildinfo) - The
.cache/directory (now deprecated for TypeScript build info) - Log files and local test results
If you need to perform a more thorough cleanup that includes global package manager caches, you can use the clean:global command. This is useful when you suspect caches are corrupted or wish to free up a large amount of disk space.
pnpm run clean:globalThis command does everything pnpm run clean does, plus:
- Purges global caches for
pnpm,npm,yarn, andbun. - Clears
pm2logs. - Removes Python-related caches from
condaandpip. - Cleans up GitHub Actions runner artifacts.
For system administrators or developers running this project on a dedicated machine, a script is available to clean up systemd journal logs. This can reclaim a significant amount of disk space but requires root privileges.
sudo ./scripts/vacuum-system-journal.shTo ensure that significant architectural decisions are well-reasoned, documented, and consistently applied, we use Architectural Decision Records (ADRs). ADRs capture the context, options considered, and rationale behind important technical choices. For more on the ADR process, see the Development Standards.
All ADRs are stored in the docs/adr/ directory.
To maintain a clean and efficient codebase, this project uses Knip to detect unused files, dependencies, and exports. For detailed configuration and usage, refer to the Development Standards.
This section clarifies when and why package.json and pnpm-lock.yaml should be modified. For specific rules on dependency changes, see the Development Standards.
We prefer using official SDKs directly whenever possible. For the Spotify integration, we use @spotify/web-api-ts-sdk.
Handling 204 No Content: Some Spotify API endpoints return a 204 No Content status on success, which can sometimes cause JSON parsing errors in certain environments or older SDK versions. We handle this using the isEmptyResponseError utility.
Consistent Targeting: All playback commands should explicitly include a deviceId when possible to ensure commands are executed on the intended device and to maintain state synchronization across multiple Spotify Connect instances.
The Spotify integration uses a "Unified Service Bus" architecture. All playback commands (Play/Pause/Skip/Volume) are dispatched from the client via WebSockets as SPOTIFY_COMMAND messages. The server handles these messages directly using the official SDK, establishing the server as the single source of truth for both playback execution and state broadcasting. This eliminates complex branching logic between local (browser) and remote (Spotify Connect) devices.
The application uses a set of stateful services (Spotify polling, Tabata timer) that are managed by the custom Express server (server.ts).
Services are initialized once during the server startup process using the createServices function in lib/services.ts. This function takes a broadcast function, which allows services to push updates to all connected WebSocket clients.
In Next.js development mode, the server-side code is frequently recompiled and re-executed. Standard singleton patterns (e.g., const service = new Service()) fail because the module is re-executed, creating multiple instances of the service. This can lead to multiple polling loops and inconsistent state.
To solve this, we use a Global Singleton pattern. Service instances are attached to globalThis, which persists across module re-executions within the same Node.js process.
- Persistence: During development,
createServiceschecks if service instances already exist onglobalThis. If they do, it reuses them instead of creating new ones. - Global Access: Attaching services to
globalThisalso allows Next.js API routes (which run in a different context than the initial server startup) to access the same service instances.
For more details on the implementation of this pattern, see TypeScript Best Practices.