Contributing to FeRx development
You don’t need to be on the core team to move FeRx forward. There are three ways to contribute, in increasing order of involvement — pick whichever fits your time and expertise. All three start or end as a GitHub Issue or PR, so the work stays visible to everyone (see Communication).
For the full process behind any of this, see the Development Lifecycle (SDLC).
Test it and tell us what breaks
The single most valuable thing most users can do is run FeRx on real models and data and report where it disagrees with NONMEM / Monolix / nlmixr2 — or where it is slow, unclear, or crashes. Parity with established tools is our top priority (see Gold-standard validation), so a concrete, reproducible mismatch is gold.
- Install per the Installation page, then run your model via the CLI or the R package (
ferx_fit()). - Compare the key outputs — OFV, theta, omega, sigma, ETAs, CWRES/IWRES — against your reference tool.
- If something is off, capture the two output sets and the versions, and open an Issue (next section). Even “it ran but the OFV is 30 points off NONMEM” is a useful report.
Open an Issue
Issues are where all work starts (see the development workflow): bug reports, parity gaps, feature requests, and docs problems all belong here.
How to write a good one:
- Search existing issues first to avoid duplicates.
- Bug: a minimal
.ferxmodel + a small data subset that reproduces it, what you expected vs. what you got, and the ferx-core / ferx-r versions. - Parity gap: attach the NONMEM (or other tool) output and the ferx output side by side, plus the model and data.
- Feature: state Why, How, and the design choices, and who benefits. Expect push-back or deferral if it serves a niche of one — see the “Christmas tree” guardrail in Philosophy and quality bar.
- Add it to the GitHub Project view so the team has visibility and can triage it.
You don’t have to fix what you file — a well-described Issue is a complete contribution on its own.
Open a Pull Request
Ready to write code or docs? The full process is in the SDLC; the short version for a contributor:
- Claim the work. Comment on the Issue you want to take (or open one first) so effort isn’t duplicated.
- Branch off the latest
main(trunk-based — see Git workflow); keep it focused on one issue. - Test it. Add a test at the right tier (see the testing pyramid); for anything numerical add a NONMEM comparison (Gold-standard validation). Every feature needs a test; every bug fix needs a regression test that fails without it.
- Keep both sides in sync. ferx-core ↔︎ ferx-r (Cross-repo synchronization) and docs updated; on the R side, regenerate
man/and keepR/*.RASCII-only (see Pull requests). - Fill every section of the PR template, including the cross-repo table.
- Expect review. Run
/code-reviewon large/numerical PRs; anything touching design, the DSL, or estimation also gets a human core-team review. - Finish the Definition-of-done checklist before marking it ready.
If the Public API baseline job goes red
The repo is a cargo workspace — the ferx-core library at the root, with ferx-tools (multi-fit tooling) and ferx-cli (the ferx binary) on top — and api/ferx-core-public-api.txt is a committed snapshot of everything ferx-core exposes. CI diffs it, so any new pub item fails that job until the baseline is updated in the same PR. That is deliberate: it makes each widening one reviewable line instead of something buried in a large change.
If the change is intended:
tools/update-public-api.sh # regenerate, then commit the resultand say in the PR description which caller needs each added item and why the existing surface does not suffice. Do not reach for #[doc(hidden)] — it hides the item from the baseline, so a test rejects it outright. See SDLC §8.7.
Writing a docs page
These pages are read by people and retrieved a section at a time by anything else — the site’s own anchor links, and agents that pull one chunk rather than a whole page. R1 enforces the size of such a chunk; this section is the convention that makes staying under it easy, and makes the result worth retrieving.
Keep rationale and validation in their own subsection
Most oversized sections are a usage explanation and its justification welded together, so a heading that reads like a how-to serves background instead — implementation reasoning, NONMEM comparisons, benchmark tables. Give the justification its own subheading. Nothing moves off the page; it just becomes separately addressable, so a reader who wants the answer can skip it and one who wants the reasoning can link straight to it.
Use the standard names. ### Rationale for why a design or a default is what it is, ### Validation (or ### Validation against NONMEM) for the parity evidence required by Gold-standard validation. Skipping and finding both work by matching the heading, so Background, Notes, Design and History defeat the point.
Leave the verdict behind. Rationale is sometimes the answer — a reader deciding whether to override a default needs the outcome even if they skip the derivation. Keep the conclusion and any scope caveats in the usage section, and link down for the reasoning:
Analytic for the combinations listed below; everything else falls back to FD. See
[Rationale](#rationale-gradient-route).
A rationale subsection is not exempt from R1. Split it when it grows, the same as anything else — otherwise the wall of prose has only been relocated under a heading nobody trims.
Two things the linter cannot see
toc-depthis 3 (docs/_quarto.yml). A subsection added under an###lands at####and drops out of the on-page TOC, which loses half the benefit of splitting. Promote the parent to##, or raisetoc-depthfor that page.- Whether the split is by concern. Six subheadings that each mean something (“Covariance / standard errors”, “ODE solver”) make a page navigable; six that say “Part 1 … Part 6” satisfy R1 and help nobody.
Good first contributions: add a NONMEM-comparison test for a model we don’t cover yet, improve a docs page, or fix a reproducible bug with a regression test. Small, well-tested PRs are the easiest to merge. Large or experimental features should be discussed in an Issue first and developed on a long-lived feature branch rather than merged in half-working.