Skip to content
@aploon

A Practical Git Workflow for Solo Developers and Small Teams

  • #git
  • #github
  • #teamwork
  • #bestpractices

Git is easy to start using, but a consistent workflow becomes increasingly important as projects grow or multiple developers contribute.

A good workflow should make changes easy to understand, reduce the risk of losing work, and support safe collaboration.

It should not introduce more process than the project needs.

For a solo developer or a small team, a lightweight workflow based on focused branches, meaningful commits, pull requests, and regular synchronization is often sufficient.

1. Keep the main branch stable

The main branch should represent a state of the project that other contributors can reasonably build upon.

A simple workflow starts by updating the local branch:

git switch main
git pull --ff-only origin main

The --ff-only option prevents Git from creating an unexpected merge commit during this pull. If the local and remote branches have diverged, Git stops and requires the situation to be resolved deliberately.

For a new task, create a separate branch:

git switch -c feature/user-profile

Use branch names that describe the work rather than the person performing it.

Examples include:

  • feature/user-profile
  • fix/login-validation
  • docs/api-setup
  • refactor/challenge-service

These naming conventions are examples, not mandatory Git syntax.

2. Make focused commits

A commit should represent a coherent change that can be understood independently.

Instead of committing an entire day's work under a message such as updates, group related modifications into meaningful units.

For example:

git add src/profile/ProfileForm.tsx
git commit -m "Add profile form validation"

Before committing, inspect the working tree:

git status
git diff
git diff --staged

These commands help distinguish unstaged modifications from staged changes and make it easier to avoid including unrelated files.

A useful commit message explains what changed. For more complex work, the commit body can explain why the change was necessary.

Avoid committing credentials, local environment files containing secrets, generated artifacts that do not belong in version control, or unrelated formatting changes.

3. Use a branch per task

A feature branch isolates work from the main development line.

A typical task cycle looks like this:

Update main
    โ†“
Create task branch
    โ†“
Implement and test
    โ†“
Commit focused changes
    โ†“
Push branch
    โ†“
Review and integrate
    โ†“
Delete completed branch

This separation is useful even when working alone. It allows experimentation without immediately changing the main branch and provides a natural point for testing before integration.

For very small documentation changes or personal experiments, a separate branch may not always be necessary. The workflow should fit the risk and collaboration needs of the task.

4. Synchronize with remote changes

In a collaborative repository, another developer may update main while your branch is in progress.

You can incorporate those changes by merging or rebasing, depending on the team's conventions.

A rebase example:

git fetch origin
git rebase origin/main

Rebasing replays your local commits on top of another base. It can create a linear history, but it also changes commit identities.

Do not casually rebase commits that other people are already using on a shared branch. If a team adopts a rebase-based workflow, contributors should understand when rewriting history is safe.

For shared branches, merging may be a more appropriate choice.

5. Resolve conflicts deliberately

Conflicts occur when Git cannot automatically combine changes.

When a rebase encounters a conflict, inspect the affected files and resolve the conflict markers carefully. Then stage the resolved files and continue:

git add path/to/resolved-file
git rebase --continue

If the operation should be abandoned, use the appropriate abort command for the operation in progress, such as:

git rebase --abort

Do not resolve conflicts by automatically choosing one side without understanding the intended behavior. Both versions may contain important changes.

After resolving conflicts, run relevant tests and inspect the resulting diff. A conflict-free merge is not necessarily a correct merge.

6. Use pull requests as a quality checkpoint

A pull request provides a place to inspect changes before integrating them into the main branch.

For a small team, the review process can remain lightweight:

  • Explain the purpose of the change.
  • Describe any important implementation decisions.
  • Include screenshots for significant UI changes where useful.
  • Identify configuration or migration requirements.
  • Run automated tests and checks.
  • Review the diff for unrelated changes or accidental secrets.

Solo developers can also use pull requests as a self-review mechanism. Opening a pull request creates an opportunity to inspect the complete change set after implementation and before merging.

7. Automate repeatable checks

Manual testing remains important, but automated checks help detect common mistakes consistently.

A repository may run checks such as:

  • Unit and integration tests.
  • Linting and formatting verification.
  • Type checking.
  • Build validation.
  • Dependency or security scanning.

These checks can be integrated into a continuous integration workflow, for example through GitHub Actions.

The precise checks should reflect the project's stack and risk profile. A small frontend application may prioritize type checking and build validation, while a backend with database migrations may require additional integration tests.

8. Choose a merge strategy and document it

Teams may choose merge commits, squash merges, or rebase-based integration.

Each option has trade-offs:

  • Merge commit: Preserves the branch's commit history and records the integration point.
  • Squash merge: Combines a branch's changes into one commit on the target branch.
  • Rebase: Replays commits onto a new base, usually producing a more linear history.

No single strategy is best for every repository. Consistency is more important than repeatedly changing strategies.

Document the expected workflow in the repository's contribution guide so that contributors know how branches are named, which checks are required, and how changes are integrated.

9. Know the recovery tools

Git provides useful mechanisms for recovering from mistakes.

git log helps inspect commit history, while git reflog records recent movements of local references and can help locate commits that are no longer visible through the current branch history.

For example:

git log --oneline --graph --decorate
git reflog

These commands are especially useful when a branch has been reset, rebased, or moved unexpectedly.

Commands such as git reset --hard and forced pushes can discard work or affect collaborators. Use them only when you understand their consequences and have verified that important changes are recoverable.

An effective Git workflow does not need to be complicated.

Keep the main branch stable, isolate meaningful tasks, create focused commits, synchronize deliberately, and review changes before integration. Add automated checks and recovery practices as the project requires them.

The best workflow is one that developers can follow consistently without slowing down everyday work.