- Pin action versions with full SHA hashes (`uses: actions/checkout@abcdef1`) — never use `@latest` or mutable tags.
- Use `concurrency` groups to cancel redundant workflow runs on the same branch or PR.
- Store secrets in GitHub Secrets — never hardcode tokens, keys, or credentials in workflow files.
- Cache dependencies (`actions/cache` or built-in `setup-*` caching) to speed up CI runs.
- Use reusable workflows (`workflow_call`) and composite actions to share logic across repos and reduce duplication.
- Set `permissions` at the workflow or job level with minimal scope — never use default write-all permissions.
- Use matrix strategies for testing across multiple versions, platforms, or configurations.
- Separate CI (test/lint/build) and CD (deploy) into distinct workflows — deploy only on main/release branches.
- Use `if: failure()` or `if: always()` for cleanup and notification steps that must run regardless of prior step results.