Claude Lessons / Guides
Claude Code Worktrees: Run Parallel Sessions Without Conflicts
The moment you run a second Claude Code session on the same repository, you have a problem: two agents
editing the same files, two sets of uncommitted changes, one branch. Git worktrees are the fix — each
session gets its own checkout and its own branch, sharing one repository history. Claude Code has a
--worktree flag that creates one for you, enforces the isolation at the tool level so a
session physically cannot write back into your main checkout, and cleans up afterward. Most guides on
this topic stop at git worktree add, which is the manual way and misses everything Claude
Code adds on top. This one covers the flag, the settings, subagent isolation, the cleanup rules, the
errors you will actually hit — and the decision most people get wrong, which is whether they wanted
worktrees at all instead of subagents or agent teams.
What a worktree is, and the problem it solves
A git worktree is a second working
directory with its own files and its own checked-out branch, backed by the same repository history,
objects, and remotes as your main checkout. It is not a clone. There is one .git
directory; the worktree just points at it.
That distinction is the whole value. A clone would give you isolation but a separate history, so you'd
be pushing and pulling to move work between them. A worktree gives you isolation and a shared
history, so a branch created in one worktree is immediately visible from the other, and
git log, git commit, and git push all behave normally from
inside it.
The Claude Code problem this solves is specific. One session refactoring the auth module and another fixing a bug in the same repository will step on each other in three ways: they overwrite each other's edits, they see each other's half-finished work as if it were the codebase, and they fight over which branch is checked out. Put each session in its own worktree and all three disappear. The sessions don't know the other exists.
WorktreeCreate and WorktreeRemove
hooks. That's covered in
troubleshooting, since it changes several behaviors.
The 60-second version
Pass --worktree (or -w) with a name:
claude --worktree feature-auth
That creates a worktree at .claude/worktrees/feature-auth/ at your repository root, on a
new branch named worktree-feature-auth, and starts Claude inside it. Run the same command
with a different name in a second terminal and you have two isolated sessions. Omit the name entirely
and Claude Code generates one for you, like bright-running-fox.
Three things to do once, before you rely on this:
-
Gitignore the directory. Add
.claude/worktrees/to your.gitignore, or every worktree's contents show up as untracked files in your main checkout. -
Accept workspace trust first. Interactive runs require
workspace trust. If you've never run
claudein that directory,--worktreeexits with an error telling you to. Run plainclaudeonce, accept the dialog, then use the flag. Non-interactive runs with-pskip the trust check. -
Decide what needs copying in. A worktree is a fresh checkout, so
.envand anything else gitignored is not there. See setting up the worktree environment.
You can also ask Claude to "work in a worktree" mid-session and it creates one with the
EnterWorktree tool. In the desktop app, pick the worktree option when
starting a session.
.claude/worktrees/ directory, Claude Code asks for your approval first — the
move takes the session's working directory, write access, and project configuration such as
CLAUDE.md and settings with it. An EnterWorktree permission rule and "don't
ask again" both fail to suppress this; only bypassPermissions mode skips it. (Before
v2.1.206, Claude could enter any existing worktree path without asking.)
Worktrees vs. subagents vs. agent teams vs. agent view
This is where most people go wrong, because four different features all get described as "running Claude in parallel" and they are not substitutes for each other. The useful way to think about it: three of them decide who coordinates the work, and worktrees decide who can touch which files. Worktrees are a layer, not an alternative.
| Feature | What it actually parallelizes | Reach for it when |
|---|---|---|
| Worktrees | File access. Each session or subagent gets its own checkout and branch. | Two things that write code are running at once on one repository |
| Subagents | Context. A delegated worker does a side task in its own context window and returns a summary. | A side task would flood your main conversation with search results, logs, or file dumps you'll never reference again |
Agent view (claude agents) |
Your attention. One screen dispatches and monitors background sessions. Research preview. | You have several independent tasks and want to hand them off, glance at status, and step in only when one needs you |
| Agent teams | Coordination. A lead splits a project, assigns it, and keeps workers in sync via a shared task list and messaging. Experimental, off by default. | You want Claude to do the project management, not just the work |
| Dynamic workflows | Scale. A script Claude writes runs many subagents and cross-checks their results. | A job outgrows a handful of subagents — a codebase-wide audit, a 500-file migration, findings that need verifying against each other |
Three combinations are worth knowing because they change what you have to do yourself:
- Agent view already uses worktrees. A session you dispatch from agent view moves into a worktree of its own before it edits files. You don't set that up.
- Agent teams do not. Teammates in an agent team are not isolated in worktrees, so you have to partition the work by file and give each teammate its own set. This is the single biggest operational difference between teams and worktree-based parallelism, and the most common source of agent-team conflicts.
- Subagents can, if you ask. They don't by default — see giving subagents their own worktrees.
There's also /batch, a bundled skill that splits one large change into 5 to 30
worktree-isolated subagents that each open a pull request. It isn't a fifth coordination style; it's a
packaged combination of subagents and worktrees, and it's the fastest way to see whether this pattern
suits your codebase without wiring anything up.
Finally: sessions in separate worktrees can still talk. Cross-session messaging lets Claude list and message your other sessions on the machine, so a finding in one worktree can be passed to another instead of you re-explaining it. Isolation of files does not mean isolation of knowledge.
Setting up the worktree environment
A worktree is a fresh checkout of tracked files. Your node_modules, your
.venv, your .env — none of it is there. Two things to do:
Install dependencies. Ask Claude to do it as its first instruction in the new session, or run your project's setup yourself in the worktree directory. There's no automation for this; it's a fresh checkout and it behaves like one.
Carry gitignored config in with .worktreeinclude. Add the file to your
project root. It uses .gitignore syntax:
# .worktreeinclude
.env
.env.local
config/secrets.json
Only files that match a pattern and are also gitignored get copied, so tracked files are never
duplicated into the worktree. This applies to every worktree Claude Code creates with git:
--worktree worktrees, subagent worktrees, and parallel sessions in the desktop app.
One pattern gotcha catches people. If you write a pattern starting with **/ and the files
you want live inside a directory that is gitignored as a whole, Claude Code copies them only
when that directory itself matches the pattern, or when the first name after the **/ is
one of the names in the directory's path. So **/.claude/skills/*.md works — the first name
is .claude, which is in the path. But **/config.json will not reach into an
ignored vendor/ directory; write vendor/**/config.json instead. (Before
v2.1.239, the reach-into-an-ignored-directory case was narrower still.)
.worktreeinclude exists because
most projects need real credentials to run, but the effect is that every parallel session gets its own
copy of your .env. Keep the list minimal, keep .claude/worktrees/
gitignored, and don't put production credentials in a file you're duplicating across a dozen
directories on disk.
How Claude Code enforces isolation
This is the part a plain git worktree add doesn't give you. Isolation by convention fails
the first time an agent runs cd ../.. and then git reset --hard. While a
session is isolated in a worktree, Claude Code blocks tool calls that would escape it. Four checks run,
and they apply whether you started with --worktree, Claude entered a worktree with
EnterWorktree, or you resumed a worktree session:
| Check | What gets blocked |
|---|---|
| File edits | An Edit, Write, or NotebookEdit targeting a path in the main checkout |
| Command working directory | A Bash, PowerShell, or Monitor command whose working directory resolves to the main checkout — or that Claude Code can't verify stays outside it |
| Git redirects | A command that points git back at the main checkout via git -C, --git-dir, GIT_DIR, GIT_WORK_TREE, or a cd before the git call |
| Command shape | Any command where Claude Code can't verify from the command text that git stays inside the worktree — a computed command name, unparseable syntax. This check cannot be turned off. |
The checks cover the repository you launched from, and also the main checkout that a linked worktree is linked from. Every subagent spawned from an isolated session inherits them, interactive or background. For PowerShell commands, only the working-directory check applies.
When a command is refused, Claude sees a tool error naming the worktree and telling it how to proceed — usually "split this into plain, separate commands." That's the fourth check firing on a clever one-liner, and the right response is a simpler command, not a workaround.
Practice parallel sessions before you trust them
Claude Lessons has free interactive lessons on running parallel sessions with worktrees, delegating to subagents, and reviewing diffs — practiced in a simulated Claude Code workspace where a bad command costs you nothing.
Start the free interactive lessonsGiving subagents their own worktrees
Subagents don't get worktrees by default, which is usually right — most subagents read and report rather than write. When several of them do write, parallel edits conflict, and worktrees fix it. Two ways to turn it on.
For a one-off, just say so: ask Claude to "use worktrees for your agents."
To make it permanent for a custom subagent, add isolation: worktree to its frontmatter in
.claude/agents/:
---
name: refactorer
description: Applies mechanical refactors across many files
isolation: worktree
---
Apply the requested refactor across every affected file, then run the tests
and report the results.
Each subagent gets a temporary worktree. Claude Code removes it automatically when the subagent
finishes without changes; a worktree that still holds work stays on disk until the periodic
sweep can remove it without losing anything. Subagent worktrees use the same base branch as
--worktree, so they branch from your repository's default branch unless you set
worktree.baseRef to "head" — which matters more than it sounds, and is the
next section.
Controlling where worktrees branch from
The base branch
By default, new worktrees branch "fresh": from your repository's default branch on the
remote, usually main. That's the right default for independent features, and the wrong
one for the case people hit first — isolating subagents that need to work on the code you have in
progress right now. Those subagents branch from main and never see your unpushed commits.
{
"worktree": {
"baseRef": "head"
}
}
"head" branches from your current local HEAD instead, so the worktree carries
your unpushed commits and feature-branch state. Inside a worktree, "head" resolves to that
worktree's HEAD, not the main checkout's.
The setting takes only those two values — you can't point it at a branch name. To start a worktree from
a specific existing branch, create it with git directly (see
below). For a "fresh" base, Claude Code keeps
origin/HEAD current: if the repository hasn't been fetched in the last 24 hours it fetches
the default branch, capped at five seconds, falling back to the locally cached ref if that fails. With
no remote configured, or no cached origin/HEAD it can fetch, the worktree falls back to
your local HEAD anyway. (Before v2.1.208, a fresh worktree just used whatever
origin/HEAD happened to be cached.)
Branching from a pull request
Pass --worktree a number prefixed with #, a GitHub pull request URL, or a
GitLab merge request URL. Quote it, or your shell reads # as a comment:
claude --worktree "#1234"
Claude Code reads only the number, fetches that change's head commit from origin, and
creates the worktree at .claude/worktrees/pr-1234. It picks the fetch path from
origin's host: pull/<number>/head on github.com,
merge-requests/<number>/head on gitlab.com, and on GitHub Enterprise, self-managed
GitLab, or anything else it tries the GitHub path first and then the GitLab one. (Before v2.1.233, only
#<number> and GitHub-style URLs were accepted.)
This is the best reason to use worktrees even if you never run parallel sessions: reviewing a colleague's PR in an isolated checkout, with Claude able to run the tests, without disturbing anything you have in progress.
Reusing a name
Passing a name whose directory already exists opens that worktree instead of creating a new one. With
the default "fresh" base, a reopened worktree resets to the default branch rather
than continuing at its old tip, but only when all three of these hold:
- It has no uncommitted changes or untracked files.
- It's still on the branch Claude Code created for it.
- It has no commits of its own, or its PR was merged and its remote branch deleted.
In every other case — any condition fails, the state can't be verified,
worktree.baseRef is "head", or the name is a PR reference — it reopens at the
old tip. Claude Code detects the merged case from git state alone: the remote branch it pushed to is
gone, and every commit in the worktree is already on the default branch. (Before v2.1.208, reusing a
name always reopened at the old tip.) The practical upshot: a worktree named bugfix that
you keep reusing quietly becomes a fresh branch each time your last PR merges, which is what you want
and a surprise the first time.
Cleanup: what disappears, what survives
When you exit an interactive worktree session, Claude checks the worktree for work that removal would destroy — changed files, untracked files, new commits — and behaves accordingly:
| State on exit | What happens |
|---|---|
| Clean, unnamed session | Claude removes the worktree and its branch automatically |
| Clean, named session | Prompts you first, so you can keep the worktree for later |
| Has work in it | Prompts you to keep or remove. Removing deletes the directory and its branch, along with everything in them |
Non-interactive -p run |
No exit prompt, so no cleanup. The worktree and the lock Claude Code took on it stay until a later session's stale-lock sweep |
Separately, a periodic sweep removes worktrees Claude created for subagents and background sessions
once they're older than your cleanupPeriodDays setting. The sweep leaves a worktree alone
when it still holds work (changed files, untracked files, or unpushed commits), when it belongs to a
--worktree session you haven't backgrounded, or when you created it with
git worktree add — even if you later ran a --worktree <name> session in
it and backgrounded that.
The mechanism behind that last one is worth knowing: Claude Code writes a marker into the git metadata
of every worktree it creates, and the sweep keeps any worktree without one. (Before v2.1.246 the sweep
didn't check for the marker and could remove a worktree you'd made yourself, if an old
background-session record pointed at it.) While an agent or backgrounded session is running, Claude Code
holds a git worktree lock on its worktree so cleanup can't remove it mid-flight — which
also means git worktree remove refuses while it runs. The sweep releases locks belonging to
sessions whose process has exited, so a killed background session doesn't strand its worktree forever
(before v2.1.210 it did). It never releases a lock you set with git worktree lock.
To remove one by hand:
git worktree remove .claude/worktrees/feature-auth
# if it has uncommitted changes or untracked files
git worktree remove --force .claude/worktrees/feature-auth
# if git refuses because the worktree is locked
git worktree unlock .claude/worktrees/feature-auth
On Windows, removing a worktree doesn't delete files outside it: if a folder inside the worktree is an NTFS junction or directory symlink, Claude Code deletes only the link and keeps the target. (Before v2.1.205, a link nested in a subdirectory could take its target with it.)
Resuming a worktree session
Resume a session that was inside a worktree and Claude Code returns it there — for interactive resumes,
for --continue and --resume in non-interactive -p mode, and for
the Agent SDK. Before doing so it verifies the worktree is still a checkout genuinely separate from the
main one, and declines if it isn't.
Three behaviors that surprise people:
-
Launch from the main checkout, not from inside the worktree. Claude Code re-enters a
worktree it created under
.claude/worktrees/even when you launch from inside it. But if you launch from inside any other worktree — one you made withgit worktree add, or a subdirectory of one — it often can't vouch for it from there and declines. Launching from the main checkout always works. -
--fork-sessiondoesn't inherit the worktree. A forked session starts in the directory you launched Claude from, and the original session's worktree is left untouched. - A deleted worktree is handled gracefully. The session resumes in your launch directory, Claude Code tells you the worktree is gone, and it clears the session's worktree binding.
When Claude enters or exits a git-created worktree, the transcript follows: Claude Code records the
session under its new working directory the same way /cd does, so /desktop
and --resume find it there. Exiting moves it back. This needs v2.1.198 or later, and a
worktree made by a WorktreeCreate hook keeps its transcript at the launch directory
instead.
An interactive resume that can't return to its worktree tells you so and continues without isolation.
Non-interactive -p runs and Agent SDK resumes are stricter: every refusal except a
missing worktree stops the resume with a stderr error rather than silently continuing
unisolated. With --output-format stream-json the refusal also arrives on stdout as a
result message with subtype error_during_execution, so an SDK application
gets the reason and not just a non-zero exit. (Before v2.1.260, no result message was
produced.)
What worktrees share with your main checkout
A worktree gets its own files and branch, but three things are deliberately shared — and each one saves you a chore:
-
The repository's
.gitdirectory. Git commands in a worktree write to the main repository's shared.git, and sandboxing allows those writes, sogit commitworks from inside a worktree even with the sandbox enabled. - Project-scope plugins. Plugins installed at project scope from the main checkout also load in worktrees of the same repository — no reinstalling per worktree. Requires v2.1.200 or later. (See the skills and plugins guide for what "project scope" means.)
-
Permission approvals. Choosing "Yes, and don't ask again" for a Bash command in a
worktree session saves the rule to the main checkout's
.claude/settings.local.json, so it applies in the main checkout and every other worktree, and survives the worktree's removal. Requires v2.1.211 or later; before that, an approval granted in a worktree stayed in it and was lost when the worktree was removed — a genuinely annoying bug if you're on an older version. On Windows and in other cases where Claude Code doesn't use the repository root, the rule still stays with that worktree.
All three hold whether the worktree came from --worktree, from git worktree add, or from the desktop app.
One thing that does not follow the worktree:
hook paths.
${CLAUDE_PROJECT_DIR} stays pointed at the project root where the session started, so a
hook command like ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh still runs the script
in the main checkout. The worktree path arrives a different way: the cwd field in the
hook's input JSON is the worktree root, and it moves again when Claude runs cd. If a hook
needs to act on the worktree, read cwd — don't assume CLAUDE_PROJECT_DIR.
When you want a worktree on a specific existing branch, or somewhere outside the repository, use git directly:
# new branch
git worktree add ../project-feature-a -b feature-a
# existing branch
git worktree add ../project-bugfix fix-issue-456
cd ../project-feature-a && claude
git worktree list
git worktree remove ../project-feature-a
Troubleshooting the errors you'll actually hit
Git LFS files are pointer files in the worktree
The most common real-world complaint, and it has a specific cause: you ran
git lfs install --local, which writes the LFS filter driver into the repository's own
.git/config rather than your global git config. Claude Code deliberately skips the
repository's own filter drivers when creating a worktree — a filter driver is a shell command, and
anything that can write to the repository (including Claude) could have put one there. A plain global
git lfs install is unaffected. Fix: run git lfs pull inside the worktree.
(Before v2.1.247, Claude Code ran those drivers during creation.)
In four rarer cases, Claude Code refuses to create the worktree at all rather than guess. Match the error:
| Error mentions | Fix |
|---|---|
| Could not read the repository git config | Fix the permissions on .git/config and retry |
A filter driver whose name cannot be neutralized (contains = or a newline) |
Rename or remove that filter driver in .git/config |
A conditional include (includeIf) |
Inline what the includeIf pulls in, remove the includeIf, retry. An includeIf in your global config doesn't trigger this |
The repository's own git config sets <key> |
The named key points Git LFS at a program to run (e.g. lfs.standalonetransferagent). If it's yours, move it to your global config. If you don't recognize it, remove it — a tool or checkout you don't trust may have written it |
"Refusing to use <path> as an isolation worktree"
Claude Code checks a directory's git identity before adopting it as an isolated checkout. The usual
cause is that the directory's git metadata resolves back into the main checkout — its .git
file points at the main repository's own .git directory, or a core.worktree
redirect resolves its working tree to the main checkout. From such a directory, an ordinary
git reset --hard would hit your main checkout. Claude Code also refuses when a
.git entry exists but can't be read, rather than assuming it's fine.
The endings that need different responses:
- "launch from the parent checkout" / "Run the resume from the project checkout" — you launched from inside the worktree. Launch from the main checkout; nothing needs recreating.
- "it contains the protected checkout" — the refused directory is a parent of your main checkout, such as your home directory. Do not delete it. Change the worktree path instead.
-
"the protected checkout <path> has a .git entry that could not be examined" /
"has git metadata that could not be resolved" — the problem is your main checkout's
metadata, not the worktree's. Don't delete the worktree, and ignore the message's trailing advice to
recreate it. Repair the main checkout (a permissions problem, a git
dubious ownershiprefusal) and retry. - "its recorded path has a network spelling" — Claude Code never resumes into a worktree at a network path. Recreate it locally.
In all cases Claude Code leaves the refused directory on disk, because it may hold work. Salvage before you delete.
Worktree creation fails on a symlinked path
Claude Code refuses to create a worktree when .claude,
.claude/worktrees, or the worktree directory itself is a symlink, and the error names the
path. Remove the symlink and retry. This is a hardening fix: before v2.1.212, a committed symlink at
one of those paths was followed, and worktree creation could write files outside the repository.
Non-git version control
For SVN, Perforce, or Mercurial, configure a WorktreeCreate hook that creates the checkout
and prints the directory path on stdout, plus a WorktreeRemove hook to clean up:
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
Three consequences of replacing the git logic, all of which have bitten people:
.worktreeincludeis not processed. Copy those files inside the hook script.- The hook must create its directories outside any git repository. If it creates them inside one, git resolves the directory to that repository's checkout and Claude Code refuses it.
- The cleanup sweep keeps hook-created worktrees, because they lack Claude Code's git-metadata marker. You own their lifecycle.
If Claude Code can't enter the worktree directory at startup it prints an error naming the path and exits with code 1 — most often because the hook printed something other than the directory it created, or the directory was deleted after setup.
Common mistakes
-
Forgetting to gitignore
.claude/worktrees/. Do this first. Otherwise every worktree's untracked files pollutegit statusin your main checkout, and Claude starts reasoning about them as if they were your project. -
Leaving
baseRefon"fresh"while isolating subagents on in-progress work. The subagents branch frommainand never see the code you're actually changing, then report confidently on the wrong version. Set"head"when the work depends on unpushed commits. -
Running
claude -p --worktreein a loop and never cleaning up. Headless runs have no exit prompt, so they leave worktrees and locks behind. Either remove them in the script or accept that the sweep gets to them eventually — but don't be surprised by fifty stale directories. - Expecting agent teams to isolate teammates. They don't. Partition the work by file or you'll get exactly the collisions you adopted worktrees to avoid.
- Treating a worktree as a code review. Isolation means a bad change can't touch your main checkout. It says nothing about whether the change is correct. You still review the diff before merging — arguably more carefully, since you weren't watching it happen.
- Running four parallel sessions on a plan that can't afford one. Parallelism multiplies token usage, and each fresh worktree means each session re-reads your project from scratch. Two well-scoped sessions usually beat four thrashing ones.
- Deleting a "broken" worktree before reading the error. Several refusal messages are about your main checkout's metadata, not the worktree's, and the worktree may hold the only copy of an hour's work. Read the ending, then act.
FAQ
How do I run two Claude Code sessions at once?
Run claude --worktree <name> in one terminal and
claude --worktree <other-name> in another. Each gets a git worktree under
.claude/worktrees/<name>/ on a new branch worktree-<name>. Edits
in one never touch the other, and Claude Code blocks tool calls that would reach back into your main
checkout. You can just open two terminals in the same directory — but then both sessions edit
the same files, and you'll get exactly the collisions worktrees exist to prevent.
Where does Claude Code put worktrees?
Under .claude/worktrees/<name>/ at your repository root. Add
.claude/worktrees/ to .gitignore. A worktree branched from a pull request
with --worktree "#1234" lands at .claude/worktrees/pr-1234 instead. To place
them elsewhere, use a WorktreeCreate hook, which replaces the default git logic entirely.
Does my .env get copied into a worktree?
Not by default — a worktree is a fresh checkout, so gitignored files are absent. Add a
.worktreeinclude file to your project root listing what to carry in. It uses
.gitignore syntax, and only files that both match a pattern and are gitignored are copied,
so tracked files are never duplicated. It applies to --worktree worktrees, subagent
worktrees, and desktop parallel sessions — but not when a WorktreeCreate hook
replaces worktree creation.
Can subagents run in their own worktrees?
Yes. Ask Claude to "use worktrees for your agents" for a one-off, or add
isolation: worktree to a custom subagent's frontmatter to make it permanent. Claude Code
removes the temporary worktree when the subagent finishes without changes; one that still holds work
survives until the cleanup sweep can remove it safely. See the
subagents guide for when delegation is worth it
at all.
Do I still need worktrees if I'm using subagents or agent teams?
They solve different problems. Subagents protect your context window, not your files. Agent teams coordinate sessions but don't isolate teammates in worktrees, so you have to partition work by file yourself. Worktrees are the file-isolation layer underneath: use them whenever two things that write code run at once on one repository.
How do I clean up Claude Code worktrees?
Exiting an interactive session handles most of it: a clean unnamed session's worktree is removed
automatically, and anything else prompts you. Headless -p runs never clean up. By hand:
git worktree remove <path>, with --force if it has uncommitted changes,
and git worktree unlock <path> first if git refuses because the worktree is locked.
Why are my Git LFS files just pointers in the worktree?
Because git lfs install --local wrote the LFS filter driver into the repository's own
.git/config, and Claude Code skips repository-defined filter drivers when creating a
worktree — a filter driver is a shell command, and anything that can write to the repo could have put
one there. Run git lfs pull inside the worktree. A global git lfs install
isn't affected.
Learn the habits, not just the flags
Claude Lessons has 38 free interactive lessons — parallel sessions with worktrees, delegating to subagents, reviewing diffs, recovering from drift — all practiced hands-on in a simulated Claude Code workspace with instant feedback.
Practice hands-on in the free interactive lessons