## Merge order Merge first; then republish the docs; then coder/coder.com#1100 (the redirects). ## Summary The nine pages under `docs/about/contributing/` are for people working in this repository, not for Coder users or administrators, so they leave the published documentation. The repository becomes their canonical home. Until now the root `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` were two-line stubs that only linked to the docs site, so contributors on GitHub were bounced out to read them. A companion PR in `coder/coder.com` adds redirects from every retired `/docs/about/contributing/*` URL to the new GitHub location. It must merge only after this PR has merged and the docs have republished. ## Changes | Old path | New path | | -- | -- | | `docs/about/contributing/CONTRIBUTING.md` | `CONTRIBUTING.md` (replaces the stub) | | `docs/about/contributing/CODE_OF_CONDUCT.md` | `CODE_OF_CONDUCT.md` (replaces the stub) | | `docs/about/contributing/SECURITY.md` | deleted; the root `SECURITY.md` already holds the policy | | `docs/about/contributing/AI_CONTRIBUTING.md` | `contributing/AI_CONTRIBUTING.md` | | `docs/about/contributing/backend.md` | `contributing/backend.md` | | `docs/about/contributing/frontend.md` | `contributing/frontend.md` | | `docs/about/contributing/modules.md` | `contributing/modules.md` | | `docs/about/contributing/templates.md` | `contributing/templates.md` | | `docs/about/contributing/documentation.md` | `contributing/documentation.md` (see note below) | For every moved file: the docs front matter is stripped and its `title` becomes the H1, relative links are rewritten for the new depth (`../../../foo` becomes `./foo` at the root and `../foo` under `contributing/`; the two links into `docs/admin/` become `../docs/admin/...`), and the docs-only `<div class="tabs">` wrapper in `CONTRIBUTING.md` is removed. `CONTRIBUTING.md` gains a short intro that links the code of conduct, the security policy and the area guides, since GitHub only auto-surfaces the root files. `docs/manifest.json` loses the About → Contributing group and `docs/images/icons/contributing.svg`, which only that group used. Inbound references updated: - `README.md`: contribution guide link now points at `CONTRIBUTING.md` on GitHub. - `docs/tutorials/example-guide.md` and `docs/install/releases/feature-stages.md`: link to `contributing/documentation.md` on GitHub, since a docs page cannot link to a repo-only file. - `docs/.style/style-guide/README.md`: the "Relationship to" section names the new location. - `scripts/docshtmlcheck/main.go`: both the package comment and the user-facing error string name `contributing/documentation.md`. - `scripts/docshtmlcheck/README.md`: relative link to the new location. - `coderd/database/migrations/migrate_test.go`: `t.Log` link now points at `contributing/backend.md#database-fixtures-for-testing-migrations`. - `.github/pull_request_template.md`: AI contribution guidelines link. - `.github/workflows/contrib.yaml`: commit-message guidelines link in the PR title check. - `.github/workflows/test-docs-preview-mapper.sh`: the mapper test case that used `docs/about/contributing/CONTRIBUTING.md` now uses `docs/admin/security/audit-logs.md`. - `.claude/docs/{ARCHITECTURE,PR_STYLE_GUIDE,WORKFLOWS,DOCS_STYLE_GUIDE}.md`: links to `CONTRIBUTING.md`. `git grep about/contributing` is empty after this change. `CODEOWNERS` had no entry for the old paths. Note on `documentation.md`: it is docs-authoring guidance and is parked in `contributing/` for now. Folding it into `docs/.style/` is tracked separately and is deliberately not attempted here. ## Testing - `go run ./scripts/docshtmlcheck` over `docs/`, and again over `contributing/`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md`: clean. - `go vet ./scripts/docshtmlcheck/...` and `go test ./scripts/docshtmlcheck/...`: 47 tests pass. - `go vet ./coderd/database/migrations/...`: clean. `go test` for that package fails to link on this machine (`tailscale.com/tstime/mono: invalid reference to runtime.nanotime1`) identically on unmodified `main`, so it is a local toolchain issue and not exercised here; the change is a string in a `t.Log`. - `pnpm check-docs` (markdownlint-cli2 and markdown-table-formatter over `docs/`, `.claude/docs/`, root `*.md`): 495 files, 0 errors. The same two tools over `contributing/*.md`: 0 errors. - Every relative link in the eight moved files resolves to an existing path (scripted check). - `bash .github/workflows/test-docs-preview-mapper.sh`: the changed `assert_maps_to` case passes; the six `resolve_ref`/`pages_for_image` failures it reports are identical on unmodified `main` on macOS. Linear: [DOCS-975](https://linear.app/codercom/issue/DOCS-975) --- 🤖 Built with AI assistance.
17 KiB
Contributing
Thank you for your interest in contributing to Coder. This guide covers how to set up a development environment, the day-to-day workflow, and what we expect from pull requests. All participation is governed by the Code of Conduct.
Area-specific guides live in contributing/:
- Backend: how the Go services fit together and how to test them.
- Frontend: the React codebase and its conventions.
- Documentation: the style guide for
docs/. - Modules and templates: contributing to the Coder Registry.
- AI contribution guidelines: rules for pull requests where an AI system is the primary author.
To report a security vulnerability, follow the security policy instead of opening an issue.
Requirements
To get started with Coder, the easiest way to set up the required environment is to use the provided Nix environment. Learn more how Nix works.
Nix
-
After you've installed Nix, instantiate the development with the
nix-shellcommand:cd ~/code/coder # https://nix.dev/tutorials/declarative-and-reproducible-developer-environments nix-shell ... copying path '/nix/store/3ms6cs5210n8vfb5a7jkdvzrzdagqzbp-iana-etc-20210225' from 'https://cache.nixos.org'... copying path '/nix/store/dxg5aijpyy36clz05wjsyk90gqcdzbam-iana-etc-20220520' from 'https://cache.nixos.org'... copying path '/nix/store/v2gvj8whv241nj4lzha3flq8pnllcmvv-ignore-5.2.0.tgz' from 'https://cache.nixos.org'... ... -
Optional: If you have direnv installed with hooks configured, you can add
use nixto.envrcto automatically instantiate the development environment:cd ~/code/coder echo "use nix" >.envrc direnv allowNow, whenever you enter the project folder,
direnvwill prepare the environment for you:cd ~/code/coder direnv: loading ~/code/coder/.envrc direnv: using nix direnv: export +AR +AS +CC +CONFIG_SHELL +CXX +HOST_PATH +IN_NIX_SHELL +LD +NIX_BINTOOLS +NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_BUILD_CORES +NIX_BUILD_TOP +NIX_CC +NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_CFLAGS_COMPILE +NIX_ENFORCE_NO_NATIVE +NIX_HARDENING_ENABLE +NIX_INDENT_MAKE +NIX_LDFLAGS +NIX_STORE +NM +NODE_PATH +OBJCOPY +OBJDUMP +RANLIB +READELF +SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP +TEMP +TEMPDIR +TMP +TMPDIR +XDG_DATA_DIRS +buildInputs +buildPhase +builder +cmakeFlags +configureFlags +depsBuildBuild +depsBuildBuildPropagated +depsBuildTarget +depsBuildTargetPropagated +depsHostHost +depsHostHostPropagated +depsTargetTarget +depsTargetTargetPropagated +doCheck +doInstallCheck +mesonFlags +name +nativeBuildInputs +out +outputs +patches +phases +propagatedBuildInputs +propagatedNativeBuildInputs +shell +shellHook +stdenv +strictDeps +system ~PATH 🎉- If you encounter a
creating directoryerror on macOS, check the troubleshooting section below.
- If you encounter a
Without Nix
If you're not using the Nix environment, you can launch a local DevContainer to get a fully configured development environment.
DevContainers are supported in tools like VS Code and GitHub Codespaces, and come preloaded with all required dependencies: Docker, Go, Node.js with pnpm, mise, and make.
For manual setup outside Nix and DevContainers, install Docker, mise, and
make. Run mise install from the repository root to install Go, Node.js
with pnpm, and development tools at the versions pinned in mise.toml.
Development workflow
Use the following make commands and scripts in development:
./scripts/develop.shruns the frontend and backend development servermake buildcompiles binaries and release packagesmake installinstalls binaries to$GOPATH/binmake testmake pre-commitruns gen, fmt, lint, typos, and builds a slim binarymake pre-commit-lightruns fmt and lint for shell, terraform, markdown, helm, actions, and typos (skips gen, Go/TS lint+fmt, and binary build)make pre-pushruns heavier CI checks including tests (allowlisted)
Install the git hooks to run these automatically:
git config core.hooksPath scripts/githooks
Hook checks are opt-in. Enable them with git config coder.pre-commit true
and git config coder.pre-push true; developers allowlisted in the hook
scripts run them by default.
The hooks classify staged/changed files and select the appropriate target.
Commits that only touch docs, shell, terraform, or other lightweight files
run make pre-commit-light instead of the full make pre-commit, and
pre-push is skipped entirely. Changes to Go, TypeScript, SQL, proto, or
the Makefile trigger the full targets as before.
Running Coder on development mode
-
Run the development script to spin up the local environment:
./scripts/develop.shThis will start two processes:
- http://localhost:3000: the backend API server. Primarily used for backend development and also serves the static frontend build.
- http://localhost:8080: the Node.js frontend development server. Supports hot reloading and is useful if you're working on the frontend as well.
Additionally, it starts a local PostgreSQL instance, creates both an admin and a member user account, and installs a default Docker-based template.
-
Verify Your Session
Confirm that you're logged in by running:
./scripts/coder-dev.sh listThis should return an empty list of workspaces. If you encounter an error, review the output from the develop.sh script for issues.
Note
coder-dev.shis a helper script that behaves like the regular coder CLI, but uses the binary built from your local source and shares the same configuration directory set up bydevelop.sh. This ensures your local changes are reflected when testing.The default user is
admin@coder.comand the default password isSomeSecurePassword! -
Create Your First Workspace
A template named
dockeris created automatically. To spin up a workspace quickly, use:./scripts/coder-dev.sh create my-workspace -t docker
Deploying a PR
You need to be a member or collaborator of the coder GitHub organization to be able to deploy a PR.
You can test your changes by creating a PR deployment. There are two ways to do this:
- Run
./scripts/deploy-pr.sh - Manually trigger the
pr-deploy.yamlGitHub Action workflow.
Available options
-dor--deploy, force deploys the PR by deleting the existing deployment.-bor--build, force builds the Docker image. (generally not needed as we are intelligently checking if the image needs to be built)-e EXPERIMENT1,EXPERIMENT2or--experiments EXPERIMENT1,EXPERIMENT2, will enable the specified experiments. (defaults to*)-nor--dry-runwill display the context without deployment. e.g., branch name and PR number, etc.-yor--yes, will skip the CLI confirmation prompt.
Note
PR deployment will be re-deployed automatically when the PR is updated. It will use the last values automatically for redeployment.
Styling
Pull Requests
We welcome pull requests (PRs) from community members including (but not limited to) open source users, enthusiasts, and enterprise customers.
We will ask that you sign a Contributor License Agreement before we accept any contributions into our repo.
Please keep PRs small and self-contained. This allows code reviewers (see below) to focus and fully understand the PR. A good rule of thumb is less than 1000 lines changed. (One exception is a mechanistic refactor, like renaming, that is conceptually trivial but might have a large line count.)
If your intended feature or refactor will be larger than this:
- Open an issue explaining what you intend to build, how it will work, and that you are volunteering to do the development. Include
@coder/community-triagein the body. - Give the maintainers a chance to respond. Changes to the visual, interaction, or software design are easier to adjust before you start laying down code.
- Break your work up into a series of smaller PRs.
Stacking tools like Graphite are useful for keeping a series of PRs that build on each other up to date as they are reviewed and merged.
Each PR:
- Must individually build and pass all tests, including formatting and linting.
- Must not introduce regressions or backward-compatibility issues, even if a subsequent PR in your series would resolve the issue.
- Should be a conceptually coherent change set.
In practice, many of these smaller PRs will be invisible to end users, and that is ok. For example, you might introduce a new Go package that implements the core business logic of a feature in one PR, but only later actually "wire it up" to a new API route in a later PR. Or, you might implement a new React component in one PR, and only in a later PR place it on a page.
Reviews
The following information has been borrowed from Go's review philosophy.
Coder values thorough reviews. For each review comment that you receive, please "close" it by implementing the suggestion or providing an explanation on why the suggestion isn't the best option. Be sure to do this for each comment; you can select Done to indicate that you've implemented the suggestion, or you can add a comment explaining why you aren't implementing the suggestion (or what you chose to implement instead).
It is perfectly normal for changes to go through several rounds of reviews, with one or more reviewers making new comments every time, then waiting for an updated change before reviewing again. All contributors, including those from maintainers, are subject to the same review cycle; this process is not meant to be applied selectively or to discourage anyone from contributing.
Releases
Coder releases are managed entirely through the
release.yaml
GitHub Actions workflow, triggered manually via "Run workflow" in the Actions
tab. Release notes are automatically generated from commit titles and PR
metadata.
Release types
| Type | Tag | Source | Purpose |
|---|---|---|---|
| RC (release candidate) | vX.Y.0-rc.W |
main or branch |
Pre-release for testing |
| Create release branch | vX.Y.0-rc.W |
main |
Cut release/X.Y + tag RC atomically |
| Release | vX.Y.0 |
release/X.Y |
First release of a minor version |
| Patch | vX.Y.Z |
release/X.Y |
Bug fixes and security patches |
Workflow
RC tags can be created from main or from a release branch. The
create-release-branch type creates release/X.Y and tags the next RC in one
step, continuing the RC numbering sequence.
main: --*--*--*--*--*--*--*--*--*--
| rc.0 rc.1 |
| +--- create-release-branch ---+
| |
| release/2.34: --*-- rc.2 -- rc.3 -- v2.34.0
|
+-- (more RCs on main for next cycle)
- RC: Go to Actions > Release,
select "Run workflow", select
main(or a release branch) from the "Use workflow from" dropdown, chooserc, and optionally provide a commit SHA (defaults to HEAD). The workflow calculates the next RC version automatically. - Create release branch: Select
mainin the dropdown, choosecreate-release-branch, and optionally provide a commit SHA. This createsrelease/X.Yand tags the next RC atomically. - Release: Select the release branch (e.g.
release/2.34) from the dropdown and chooserelease. No other inputs needed. - Patch: Cherry-pick fixes onto
release/X.Y, select that branch from the dropdown, and chooserelease.
The workflow validates that commits are on the expected branch for each release type.
Retrying a failed release
If the
release.yaml
workflow fails after the tag has been pushed, re-run the failed jobs from the
GitHub Actions UI. The prepare-release job is idempotent and will detect
the existing tag.
To test the workflow without publishing, select dry-run.
Commit messages
Commit messages should follow the Conventional Commits 1.0.0 specification.
Allowed commit types (feat, fix, etc.) are listed in
conventional-commit-types.
Note that these types are also used to automatically sort and organize the
release notes.
A good commit message title uses the imperative, present tense and is ~50 characters long (no more than 72).
Examples:
- Good:
feat(coderd): add feature X - Bad:
feat(coderd): added feature X(past tense)
Scopes must reference a real path in the repository (a directory or file stem)
and must contain all changed files. For example, use coderd/database if all
changes are within that directory. If changes span multiple top-level
directories, omit the scope.
A good rule of thumb for writing good commit messages is to recite: If applied, this commit will ....
Note: We lint PR titles to ensure they follow the Conventional Commits specification, however, it's still possible to merge PRs on GitHub with a badly formatted title. Take care when merging single-commit PRs as GitHub may prefer to use the original commit title instead of the PR title.
Backporting fixes to release branches
When a merged PR on main should also ship in older releases, add the
backport label to the PR. The
backport workflow
will automatically detect the latest three release/* branches,
cherry-pick the merge commit onto each one, and open PRs for
review.
The label can be added before or after the PR is merged. Each backport
PR reuses the original title (e.g.
fix(site): correct button alignment (#12345)) so the change is
meaningful in release notes.
If the cherry-pick encounters conflicts, the backport PR is still created with instructions for manual resolution; no conflict markers are committed.
Breaking changes
Breaking changes can be triggered in two ways:
- Add
!to the commit message title, e.g.feat(coderd)!: remove deprecated endpoint /test - Add the
release/breakinglabel to a PR that has, or will be, merged intomain.
Generative AI
Using AI to help with contributions is acceptable, but only if the AI Contribution Guidelines are followed. If most of your PR was generated by AI, please read and comply with these rules before submitting.
Security
Caution
If you find a vulnerability, DO NOT FILE AN ISSUE. Instead, send an email to security@coder.com.
The
security
label can be added to PRs that have, or will be, merged into main. Doing so
will make sure the change stands out in the release notes.
Experimental
The
release/experimental
label can be used to move the note to the bottom of the release notes under a
separate title.
Troubleshooting
Database migration mismatch after switching branches
If ./scripts/develop.sh exits with a "database migration conflict" error,
it means the database has migrations from another branch that don't exist
on the current one. You have two options:
# Roll back the mismatched migrations (preserves your dev data):
./scripts/develop.sh --db-rollback
# Or wipe the database and start fresh:
./scripts/develop.sh --db-reset
Nix on macOS: error: creating directory
On macOS, a direnv bug can cause
nix-shell to fail to build or run coder. If you encounter
error: creating directory when you attempt to run, build, or test, add a
mkdir line to your .envrc:
use nix
mkdir -p "$TMPDIR"