If you're a solo developer, consider this simple setup.
1. Use the package npm-run-all to run scripts locally in parallel.
2. Add Husky to connect common scripts in your app (format, lint, tests, build, type-check, unit tests, component tests) and run them on commit.
3. Commit directly to main — this triggers an automatic deployment to production.
4. Use pnpm - it's just really good :D.
Being a solo dev these days doesn’t require a complex CI/CD setup. You can run all the basic checks locally to avoid context switching, and let Vercel, Netlify, or Cloudflare handle automatic deployments to production after each commit to main. This setup works perfectly with trunk-based development and feature flags.
I use this approach for every new app I build.
1. Working on a feature that’s not finished yet? Add a feature flag, commit, and it auto-deploys to production.
2. When the feature is ready, commit again — it auto-deploys, and the feature becomes visible.
3. On each commit, tests and checks run automatically (usually 30–45 seconds for larger projects). No context switching — everything happens locally on my machine.
4. This setup can be tricky with E2E tests (you might need Docker to ensure consistent results). Still totally doable — just requires slightly different scripts and setup.
5. Husky connects to the commit hook and runs checks. I also added another hook to enforce conventional commits.
This setup is small, fast, and simple — no unnecessary branches or merges. It’s perfect for solo developers, and it even works for small teams (1–2 devs). Just keep in mind: anyone (even you 😄) can bypass it if needed.
You can adjust it however you prefer, but here’s my current setup:
1. Format + Lint → runs via lint-staged, after "git add ." command.
2. Type-check → runs on commit message command, together with commitlint for commit message validation.
3. Build + Tests → run on the pre-push phase.
Here how it looks in practice:
```
git add . # Runs Format + Lint via lint-staged only changed files
git commit -m 'feat(app): my feature name' # Runs TypeCheck + Conventional Commit check
git push # Runs units, build, component tests (all parallel)
```
This setup keeps development fast, iterative, and atomic, while still preventing most regressions.
If you're interested in, I can craft nice/short article + add repo guide if you want.
I would add two more commands, also very useful: git rebase <branch> In case you work on one feature, but the target branch changed radically, affecting your work. Better than merge since after dropping few your commits, commit history is linear, simplifying traversing between your changes to current version of target branch. Merge makes it more difficult when there is a need to modify one of the previous commits on feature branch.. git cherry-pick <commit-hash> Same scope of changes between multiple branches but none of the branch is ready to be merged to main? This command allows adding existing commit from other branch to the current one. Might be very useful when many dependencies within team exists, and one of the collaborators did something in particular commits you also need.