denoland/deno
Development workflow
Day-to-day Git workflow, the build/test loop, and how to stage a change for review.
Branching
Deno follows a standard GitHub flow on top of main:
- Create a feature branch off
mainwith a descriptive name (feature/...,fix/...,perf/...). - Commit incrementally as you work — small, descriptive commits are easier to review.
- Push the branch and open a PR against
denoland/denomain. - Iterate based on review feedback by adding more commits, never by force-pushing. Everything gets squashed at merge time.
Drive-by changes belong in a separate PR. The maintainers will routinely ask you to split unrelated edits.
The build/test loop
graph LR
Edit["Edit code"] --> Format["./x fmt"]
Format --> Build["cargo build --bin deno"]
Build --> Run["./target/debug/deno run case.ts"]
Run --> Test["cargo test relevant_test_name"]
Test --> Lint["./x lint or ./x lint-js"]
Lint --> Verify["./x verify"]
Verify --> Commit["git commit -m '...'"]Tips for keeping the loop fast:
cargo build --bin denois the fastest "full" build because it skips the rest of the workspace.cargo check -p <crate>is even faster when you just want a type-check on a single crate.--features hmrskips snapshot regeneration so changes to extension JS take effect without rebuilding Rust:cargo run --features hmr --bin deno -- run case.tssccacheandmold(Linux) noticeably speed up Rust compiles. Addcargo-watchif you want incremental rebuild on save.
Where to put new code
| What you're adding | Goes in |
|---|---|
| A new CLI subcommand | Define flags in cli/args/flags.rs; handler in cli/tools/<name>/ or cli/tools/<name>.rs; wire into cli/lib.rs run_subcommand. |
| A new flag on an existing subcommand | cli/args/flags.rs and the relevant cli/tools/<name>/. |
| A new web platform / Deno API | Find the right ext/<name>/. Add ops in a *.rs file, add the JS surface in a numbered *.js file, register in lib.rs. |
| A Node compatibility shim | ext/node/polyfills/<module>.ts (or .js). For node:crypto it goes into ext/node_crypto/; node:sqlite into ext/node_sqlite/. |
| A change to module resolution | cli/module_loader.rs for CLI-specific behavior; libs/resolver/ or libs/node_resolver/ for the shared algorithm. |
| A change to the lockfile or npm cache | libs/lockfile/, libs/npm/, libs/npm_cache/, libs/npm_installer/. |
| A change to type checking | cli/type_checker.rs, cli/tsc.rs, or — for the new TypeScript-Go path — libs/typescript_go_client/. |
| LSP behavior | cli/lsp/. |
| Build / format / lint tooling | tools/. The CI generators are under .github/. |
Tests come first
When fixing a bug, write the failing test before the fix. When adding a feature, add at least one spec test demonstrating the expected behavior. The reviewer will look for this; PRs without tests for user-visible behavior are routinely sent back. See Testing for the test categories and how to run each.
Commit style
Looking at the recent log, the repo uses Conventional Commits with scopes:
feat(install): added --prod to skip dev deps and @types (#33248)
fix(ext/node): align node:module behavior with Node (#33482)
perf(ext/web): use simdutf for base64 encode/decode (#32743)
test: enable parallel/test-dns-lookup-promises-options-deprecated.js (#33710)
chore: clean up WPT runner output (#33487)Common types: feat, fix, perf, chore, test, refactor, docs. Common scopes: cli, ext/node, ext/web, ext/fs, runtime, lsp, npm, jsr, install, compile, bundle. Match the form of nearby commits when in doubt.
Pre-commit gate
./x verify runs the same fmt + lint-js gate that the PR workflow's first job runs. Run it locally before pushing to avoid round-trips through CI. If you touched Rust, run ./x lint (which adds cargo clippy) instead.
The slower checks (cargo build, full test matrix, OS-specific tests) run in CI and don't need to be repeated locally before pushing — fix things on the second pass after CI tells you what broke.
When CI fails
CI is generated from .github/workflows/*.ts files; the actual YAML in .github/workflows/*.generated.yml is committed but you should never edit it by hand. If you need to change CI behavior, edit the .ts file and re-run the generator script (typically deno run -A .github/workflows/<name>.ts produces the corresponding .generated.yml).
For flaky tests in CI, the recommended order:
- Re-run the failing job — racy tests do exist.
- Run the same test locally with the same flags.
- Check
tests/specs/<name>/for[UNORDERED_*]blocks if the failure is "lines in different order." - If you're confident the test is flaky and not your bug, mark it and open an issue.
Updating dependencies
Cargo updates are typically straightforward:
cargo update
cargo update -p <crate> # update one
cargo upgrade # requires cargo-edit
cargo outdated # requires cargo-outdatedCargo.lock is committed; check it into your PR. Workspace-level pins for deno_* crates live in the [workspace.dependencies] block of Cargo.toml.
Useful environment variables
| Variable | Effect |
|---|---|
DENO_LOG=debug |
Enable debug logging |
DENO_LOG=deno_core=debug |
Enable per-module logging |
RUST_BACKTRACE=1 |
Include backtrace on panic; =full for everything |
V8_FROM_SOURCE=1 |
Build V8 from source instead of downloading prebuilt |
DENO_DIR |
Override Deno's cache directory |
See Debugging for more on tracing runtime behavior.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.