How to Build a Reliable Developer Workflow with Taskfile, Pre-commit Hooks, and Git Worktrees
How to Build a Reliable Developer Workflow with Taskfile, Pre-commit Hooks, and Git Worktrees
A strong developer workflow is less about “moving faster” and more about removing small sources of friction before they turn into bugs, context loss, or repeated setup mistakes. This guide shows how to combine Taskfile, pre-commit hooks, and Git worktrees into one practical system you can use on real projects.
Why this workflow works
The core idea is to make the common path obvious: one command to run tasks, one automatic gate before commits, and one isolated directory per piece of work. Taskfile helps you standardize project commands so people do not need to remember long shell snippets. Pre-commit hooks catch formatting and quality problems before they enter the repository, and Git worktrees let you work on multiple branches without constantly stashing or switching folders.
This matters because many workflow problems are really coordination problems: “What command do I run?”, “Did I format this?”, and “How do I keep two fixes separate?”. When those answers are encoded in files checked into the repo, the process becomes repeatable for you and for everyone else.
Step 1: Create a task runner
Start by putting your most-used commands into a Taskfile.yml. Task is designed to wire together repetitive actions like code generation, formatting, linting, and tests, while keeping the workflow discoverable from anywhere in the project. A good Taskfile should be short, readable, and predictable.
version: '3'
tasks:
setup:
desc: Install dependencies
cmds:
- npm install
format:
desc: Format the codebase
cmds:
- npm run format
lint:
desc: Run lint checks
cmds:
- npm run lint
test:
desc: Run tests
cmds:
- npm test
ci:
desc: Run the local CI sequence
deps: [lint, test]
With this in place, you can run task setup, task lint, or task ci instead of remembering the exact package-manager command. The real win is consistency: new teammates and future-you get the same entry points every time.
A practical rule
Keep the task names stable and the descriptions explicit. Taskfile’s built-in descriptions make it easier to discover available commands, which helps new contributors quickly understand the project’s workflow. If a command is painful to type or easy to forget, it belongs in the Taskfile.
Step 2: Add pre-commit checks
Pre-commit hooks are your safety net for local quality control, because they run automatically before a commit is finalized. They are best used for fast checks: formatting, linting, small validation scripts, and lightweight tests. The goal is not to replace CI; it is to stop obvious problems before they leave your machine.
A simple .pre-commit-config.yaml can look like this:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: check-yaml
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.13.0
hooks:
- id: eslint
additional_dependencies: []
Install and activate the hooks once per clone:
pre-commit install
pre-commit run all-files
A good hook set should be fast enough that you barely notice it during normal work. If a check is slow, move it to Taskfile or CI so commits stay responsive.
Step 3: Use worktrees for parallel work
Git worktrees let one repository have multiple checked-out branches in separate directories. That makes them perfect for situations where you are fixing a bug, reviewing a PR, and starting a new feature on the same day. Instead of stashing changes repeatedly, you can keep each task isolated and open in its own editor window.
A simple setup looks like this:
git worktree add ../project-auth feature/auth
git worktree add ../project-fix-login hotfix/login-edge-case
git worktree list
A good habit is to name the folder after the branch or task so it is obvious what lives there. When you are done, clean up with:
git worktree remove ../project-auth
git worktree prune
This is especially useful if you use separate terminals or AI tools per branch, because it reduces cross-branch mistakes and watcher collisions.
Step 4: Connect the pieces
The workflow becomes powerful when these tools reinforce each other. Taskfile gives you a single command surface, pre-commit enforces local quality, and worktrees give each task its own space. That means you can open a fresh worktree, run task setup, make your change, and commit only after the hook checks pass.
A typical day might look like this:
- Create a worktree for the feature.
- Run
task setupin that directory. - Make the change in small steps.
- Run
task lintandtask test. - Commit, letting pre-commit catch formatting drift.
- Remove the worktree after merge.
This pattern reduces the number of decisions you have to make during focused work, which is often where productivity gains really come from.
Step 5: Keep tasks and hooks fast
Fast feedback is the difference between a workflow people use and a workflow they bypass. Make your hook chain short, and avoid stuffing expensive integration tests into the commit path. Reserve heavier checks for task ci or the actual CI pipeline so your local loop stays tight.
A useful split is:
- Pre-commit: formatting, linting, file hygiene, tiny validation checks.
- Taskfile local commands: setup, test, build, generate, CI simulation.
- CI: full test matrix, deployment checks, and slower verification.
If a check keeps failing for non-obvious reasons, simplify it. A workflow that is reliable but slightly less ambitious is better than a clever one that developers work around.
Example project setup
Here is a compact pattern you can drop into many JavaScript or TypeScript repos:
### Taskfile.yml
version: '3'
tasks:
setup:
cmds:
- npm install
dev:
cmds:
- npm run dev
format:
cmds:
- npm run format
lint:
cmds:
- npm run lint
test:
cmds:
- npm test
check:
deps: [format, lint, test]
### .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-yaml
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.13.0
hooks:
- id: eslint
### one-time per clone
pre-commit install
### normal use
task setup
task check
git worktree add ../my-feature feature/my-feature
This gives you a clean path from checkout to commit without relying on memory or tribal knowledge.
Common mistakes
One mistake is using Taskfile as a dumping ground for every shell script in the repo. The better approach is to expose only the commands people actually need often, and keep the names obvious. Another mistake is making pre-commit too heavy, which leads to no-verify habits and defeats the point. A third is creating lots of worktrees without cleaning them up, which makes the file system and your mental model both messy.
A good workflow is not about adding tools for their own sake. It is about turning repeated decisions into defaults so you spend more attention on the code itself.
A simple rollout plan
If you are introducing this in an existing repo, do it in this order: first add Taskfile commands for setup, lint, test, and CI-like checks. Next add a small, fast pre-commit configuration that enforces formatting and obvious hygiene. Finally, start using worktrees for parallel branches or hotfixes, and document the commands in the repository README.
That staged rollout keeps the adoption cost low and makes each improvement easy to evaluate. Once the workflow is visible in the repo, it becomes part of the codebase instead of a personal preference.
-
Rizwan Saleem | https://rizwansaleem.co
Top comments (0)