A short walk from a fresh machine to a productive REPL session inside any CliMA Julia package. This guide is intentionally generic; repo-specific quirks live in the package's own *_specific.md guide.
- Julia. Install via
juliaup.juliaup add releaseinstalls the current stable channel; CliMA repos test on the current LTS and one or two newer point releases. Check.github/workflows/ci.ymlof the repo you're working in for the exact matrix. - Git and a GitHub account with an SSH key (see GitHub's SSH setup guide).
- (Optional but recommended) a Julia-aware editor: VS Code with the Julia extension, Helix, or Emacs/Vim with
julia-mode/julia-vim.
git clone git@github.com:CliMA/<repo-name>.jl.git
cd <repo-name>.jl
julia --projectInside the REPL:
using Pkg
Pkg.instantiate() # download every dep at the manifest-pinned version
Pkg.status() # sanity-check the resolved versions
import <RepoName> # confirm the package loadsIf Pkg.instantiate() fails, see §8 below for the standard recovery sequence.
Julia's first-call latency comes from compilation. Restarting the REPL throws that work away. The two pieces of standard tooling that let you iterate without restarting:
- Revise.jl: watches package source files and patches updated method definitions into the running session.
- Infiltrator.jl: drops you into an interactive REPL at a
@infiltratebreakpoint without instrumenting the function, so it does not invalidate compiled code.
Install both into your base (v1.x) environment so every REPL gets them automatically:
julia -e 'using Pkg; Pkg.add(["Revise", "Infiltrator"])'Then add a startup file at ~/.julia/config/startup.jl:
using Revise
using InfiltratorNow your normal loop is: start the REPL once, using <RepoName>, edit code, re-run, with no restart needed.
CliMA repos use JuliaFormatter, invoked from the repo root:
using JuliaFormatter
format(".")CI pins a specific JuliaFormatter major version that varies between repos. See code_style.md §1 for the version-matching procedure and the recommended pre-commit hook.
Prefer rebasing over merging to keep history linear:
git fetch origin main
git rebase origin/mainWhen starting a new task, base your branch on the latest remote main:
git stash
git checkout main
git pull origin main
git checkout -b <initials>/<short-description> # e.g. ts/fix-precip-bug
git stash popEach commit should be a logical unit of work and keep the model compilable.
A typical first PR follows this rhythm:
- Branch:
git checkout -b <initials>/<short-description>(e.g.ts/fix-precip-bug). - Make changes; iterate in the REPL with Revise.
- Run the package's tests:
Pkg.test()(prefer this over manuallyincludeingtest/runtests.jl, sincePkg.testactivates the test environment with the test-only deps). - Format:
using JuliaFormatter; format("."). - Add a
NEWS.mdentry if the change is user-visible. See changelogs_and_versions.md. - Commit, push, and open the PR. The repo-specific guide names the canonical CI driver and the test groups that should be green before review.
For PR-review conventions, see review.md. For what AI agents may and may not do without explicit approval, see agent_autonomy.md.
When a feature is deprecated or removed, follow the full cleanup protocol:
- Source removal: delete implementation code, structs, and methods.
- Configuration purge: remove options from config files and parsers; ensure that choosing a removed option triggers a clear
errorlisting valid alternatives. - Test cleanup: delete targeted tests; update integration tests to use supported alternatives. Mirror changes between
src/andtest/. - Dependency slimming: if a package was used only by the removed feature, drop it from both
[deps]and[compat](dependency_management.md §5). - Documentation update: update docstrings and docs pages to reflect the removal.
NEWS.mdentry: under![][badge-💥breaking]if it was a public surface (changelogs_and_versions.md).
Pkg occasionally fails to find a satisfiable version set, typically after a [compat] change. The cheapest-to-most-expensive recovery sequence:
import Pkg
Pkg.instantiate() # 1. make the manifest match Project.toml
Pkg.resolve() # 2. re-run the resolver against current compat bounds
Pkg.update() # 3. move every direct dep to its newest compat-allowed versionIf those do not converge, one package is usually pinned at a version that no longer fits. Remove and re-add it so the resolver picks a fresh version:
Pkg.rm("OffendingPackage")
Pkg.add("OffendingPackage")Two mutually-constraining packages should be removed and re-added together. Pkg.status() shows current pins; Pkg.resolve() prints the resolver's diagnostic. Read that before guessing.
These all live in your base environment, not the project's:
- TestEnv.jl:
using TestEnv; TestEnv.activate()makes the test-only deps available in an interactive REPL, so you can debug a failing test withoutPkg.test's startup cost. - BenchmarkTools.jl:
@benchmarkfor measuring time and allocations of hot-path code. See allocation_debugging.md §5. - LiveServer.jl:
servedocs()builds the docs site locally and auto-reloads on file changes. - About.jl:
about(x)summarizes any value's type, memory layout, and methods. - OhMyREPL.jl - provides syntax highlighting within the REPL
- Add
using OhMyREPLto~/.julia/config/startup.jlto ensure it loads with every REPL session.
- Add
- Kaimon.jl - provides AI agents with direct access to the Julia REPL, with your standard tools like
ReviseandInfiltratorfor quick iteration and debugging
- The Julia manual: docs.julialang.org. The Variables through Documentation sections cover what you need for day-to-day work.
- Modern Julia Workflows: a current, opinionated guide to REPL-driven Julia development.
- The Julia performance tips: read once when you start writing hot-path code; type_stability.md and gpu_performance.md are the CliMA-specific extension.
- For ecosystem-wide conventions (
Y/Yₜ/pstate,ᶜ/ᶠnotation, module aliases), see ecosystem_conventions.md.
If this guide is discovered to be stale or missing a pattern, update it.