facu.azcue.dev logo

Full Stack Project Structure with Separate Branches for Code and Static Builds

A simple Git workflow to separate code and static builds for clean full-stack deployments.

This guide walks you through a simple and long-lasting way to organize full-stack projects: one branch for source code and another for the generated static site.

This approach keeps development and deployment cleanly separated — perfect if you want to serve static builds on GitHub Pages, Netlify, Cloudflare, Vercel (static mode), or similar platforms.

The Idea in 30 Seconds

  • main (or dev) → contains the project’s code (frontend + backend).
  • static → contains the static build output generated by your frontend.
  • Use git worktrees to map each branch into its own directory inside the same repo.

Folder Structure

<bash>
1/projectName
2 /fullstack ← main branch (code)
3 /frontend ← your UI app (exportable to static)
4 /backend ← your API / CMS / headless service
5 /static ← static branch (build artifacts)

Why put /static alongside /fullstack?
Because each directory is tied to a different branch in the same repo. That way, build artifacts don’t pollute your codebase, and the static site history lives independently.

Step by Step

1 - Create a remote repo

Create a new empty repository on GitHub (or any git hosting platform).

2 - Create directories

<bash>
1mkdir -p projectName/fullstack/frontend projectName/fullstack/backend projectName/static
2cd projectName/fullstack

3 - Initialize Git: code branch

<bash>
1git init
2git remote add origin https://github.com/YOUR_USERNAME/REPOSITORY_NAME.git
3echo "# Project" > README.md
4git add .
5git commit -m "init: fullstack layout"
6git branch -M main
7git push -u origin main

IMPORTANT: replace YOUR_USERNAME and REPOSITORY_NAME

4 - Create the static branch

<bash>
1git switch --orphan static
2git commit --allow-empty -m "init: static branch"
3git push -u origin static

5 - Link /static to the static branch with worktrees

<bash>
1git switch main
2git worktree add ../static static

Now you have:

  • /fullstackmain branch
  • /staticstatic branch

Tip: git worktree list shows all active mappings.

Generic Workflow

  1. Develop in /fullstack (branch main).
  2. Build your frontend (each framework has its own build command).
  3. Move the output (e.g. out/, dist/, build/) into /static (branch static).
  4. Commit + push to static. Done — your static site is ready to deploy.

Example

Works with frameworks that export plain HTML: Next.js with next export, Astro, SvelteKit static, etc.

<bash>
1# inside /fullstack/frontend
2npm run build # generates 'out'
3
4# move output into /static
5cd ../../static
6rm -rf out
7mv ../fullstack/frontend/out .
8git add .
9git commit -m "release: new static build"
10git push origin static

What About the Backend?

The backend (API, headless CMS, etc.) lives under /fullstack/backend and is versioned in main.

  • If your frontend consumes the API at build time (SSG), the static branch is self-contained: just rebuild when content changes.
  • If you need SSR or APIs at runtime, you can still use this setup for your static parts (docs, landing pages, blogs), while deploying the backend separately.

Best Practices

  • .gitignore
    • In main: ignore build directories (node_modules, .next, dist, etc.).
    • In static: do not ignore them — the build output is the content.
  • Environment variables
    • Keep secrets outside the repo (Vault, Doppler, GitHub Secrets).
    • Maintain a .env.example with documented keys only.
  • Commit discipline
    • main: messages about code changes.
    • static: messages about releases/deploys.
  • Rollback
    • Resetting static to a previous commit instantly restores the published site.

Recap

  • Use two branches: one for code (main), one for static builds (static).
  • Map them to different directories with git worktree.
  • Build frontend → move output to /static → commit + push.
  • Works with any framework that can export HTML or static files.
SSGFull StackGitGit Worktree