Contributing#
SpatialData is shaped by the people who use it. Whether you write a reader in another language or just find a sentence here unclear, every contribution counts, from a typo fix to a new element type; you do not need to be a SpatialData developer, and questions and half-formed ideas are welcome. The quickest way in is to say hello on the #spatialdata-dev channel of the scverse Zulip.
How to take part#
Discuss an idea on Zulip, or suggest a change in a GitHub issue, ideally together with a pull request.
A particularly large feature or a big breaking change comes with an RFC (see RFCs). Once approved, a change lands in the next version of the specification (see Upcoming version (0.5)); the Compatibility matrix records which implementation releases support it.
A change to the in-memory format of one language does not always require changes in the others, but sometimes it affects several languages. If in doubt, open an issue in the corresponding implementation repository, or tag its developers, to make them aware:
Python: scverse/spatialdata
JavaScript: Taylor-CCB-Group/SpatialData.js
RFCs#
An RFC (request for comments) is a written proposal that is discussed and refined in the open before anything is decided. All RFCs are listed on the RFC page, whatever their status, as the record of a design and the reasons for it.
When to write an RFC#
The change adds a concept to the abstract data model, or changes the meaning of an existing one.
The change needs a new storage format version and affects several element types, or affects readers and writers in more than one language.
There are several reasonable designs, and the choice needs input from the community.
Process#
An RFC is decided before the specification is changed, so that a design is never revised in two places at once. The figure shows who takes each step and how the status of the RFC changes.
Pitch. Float an idea on Zulip or in an issue, without a full RFC yet; it can already be listed on the RFC page.
Plan. Agree with the developers to write up the proposal.
Propose. Open a pull request against the branch
rfcsthat addsrfcs/<number>-<name>.mdfrom the template. The RFC explains the motivation, the design, the alternatives considered and the implications, in enough detail to decide on the design and later specify it.Announcement. The pull request is merged, so that the RFC appears on the RFC page; one GitHub issue is opened for its comments, linked from its Discussion section, and comments are called for on Zulip.
Discuss. Comments go to that issue; feedback from Zulip, calls or elsewhere is summarized there, and a comment on a passage can be a pull request against the RFC file. Until the decision, the RFC is the only place the design is written down; no pull requests against the specification are opened yet. Prototype implementations are welcome to test feasibility, linked from the Implementation section.
Revise. Update the RFC by further pull requests to its file; a substantial revision is announced again. The authors can also withdraw the RFC.
Decision. Once the comments have been considered, the RFC is decided following the scverse governance, and the decision, its reasons and the discussion are summarized in the Discussion section. An acceptance agrees on the design, not on a release, and names who writes the specification pull requests, by default the RFC authors.
Specify. Open pull requests for the accepted design against
storage-format, the draft of the next version (see Upcoming version (0.5)), and, if the data model changes, againstdata-model(see Versions and releases); link them from the Implementation section of the RFC. A substantive change of the design goes back into the RFC first, as a revision.Review. The pull requests are merged after an approving review by another developer, which does not reopen the decision. Editorial clarifications are settled here, and the RFC is updated where needed; requested changes go back to Specify.
Release. A release includes only specified changes that meet its requirements (see Versions and releases). Before a version is released, every accepted RFC is either specified in it or moved to a later version, noted in the RFC, so that none waits without a plan. When the version is released with an implementation that reads and writes it, the RFC links to that version.
Versions and releases#
The specification has two parts, each with its own versions and its own branch: the abstract data
model on data-model, the storage
format on storage-format. Each
storage format version encodes one data model version, and its pages include the text of that data
model version (see Structure of the specification pages): the website builds them together, so that the
data model is written once, for every storage format that encodes it. Releases are tags
data-model-<version> and storage-format-<version>, the text as first released; they are never
moved.
Each branch holds the current version of its part, with the corrections made since its release:
bug fixes to the prose, clarifications and non-normative additions, which do not open a new version.
They are pull requests against the branch, and the website shows them once merged. Older versions
stay as tagged; a correction to one, if needed, goes into a branch
maintenance/storage-format-<version> (or maintenance/data-model-<version>) made from its tag,
which the website then shows. A correction that touches both parts is two pull requests, one
against each branch, that link to each other.
A change that needs a new version makes the branch the draft of that version. Before its first such
change, the current version gets its maintenance branch, which the website shows from then on. The
pull request also
gives the draft’s container format version a -dev suffix, marks the version as draft in the version
history, and lists the change in Upcoming version (0.5). Further changes for the same version edit the
draft.
When a new version starts#
The specification decides when a version starts and when it is released, following the decision process, independently of any implementation. A new version is opened when at least one of these is true:
a change to the stored bytes is needed (new key, new payload, changed meaning of an existing key);
a new element type is introduced;
an upstream standard the format depends on (NGFF, AnnData, GeoParquet) releases a version worth adopting, such as NGFF 0.6 with RFC-5 transformations.
Release#
A version is released once its accepted changes are complete and implemented in at least one implementation, with regenerated example stores, and no accepted RFC is left without a plan (see RFCs). Then, in one pull request:
its container format version loses the
-devsuffix;Storage format version history (and Data model version history, if the data model changes) list it as current, and the previous version as deprecated;
the commit is tagged
storage-format-<version>(anddata-model-<version>ondata-model, for a new data model version), andversions.tomlon the branchsitelists it, built from the branch, which publishes it on the website.
The normative content of a released version is not changed afterwards; corrections are made as described above. Implementations adopt the version in their own releases; the Compatibility matrix records which releases read and write it.
Structure of the specification pages#
data-model.md, on the branch data-model, holds the
abstract data model. Each section’s text, below its heading, sits between two comment markers
named after the section’s label:
(spec-tables)=
## Tables
% begin spec-tables
...
% end spec-tables
storage-format.md, on the branch storage-format,
has the same sections. Each one repeats the heading, includes the data model text between the
markers, and follows it with the storage format; the website build puts data-model.md next to
it:
(storage-tables)=
## Tables
:::{include} data-model.md
:start-after: % begin spec-tables
:end-before: % end spec-tables
:::
(storage-tables-layout)=
### Layout
...
A change to the data model is therefore made once, in data-model.md, and appears on the data
model page and on the storage format page of every version that encodes it. Moving or renaming a
marker changes what the storage format pages include: the preview of a pull request against
data-model builds them all, so that a broken include shows there.
On the storage format page, the included text is marked Data model with a line beside it.
Data model sections are normative, and readability comes first: plain, human-readable English for everyone. Prefer positive statements. Keep implementation details out; move history and outlook into footnotes (
[^name]), with the footnote text inside the section’s markers so that it is included with it.Storage format sections are normative and must be unambiguous. They state only what the storage format adds, and link to the data model for the concepts (
{ref}to thespec-*anchors) instead of repeating them. Give every subsection heading astorage-*label.Section headings cannot go inside tabs (such as the transformation types). There, use a rubric with a label, which gets a “¶” permalink that also records the selected tabs:
(storage-tables-layout)= ```{rubric} Layout ```Figures go in
img/and are included with{figure},:figclass: spec-figureand an:alt:text.
Writing rules#
Use the RFC 2119 key words MUST, SHOULD and MAY in plain capitals (not bold), and mean them: always in the storage format, in the data model only where they make a constraint clearer.
Specify only what NGFF, AnnData, Zarr, Parquet or GeoParquet leave open; link to them for the rest.
Write the pages as the specification of their version alone, without referring to other versions; the version history is the only place that compares versions.
The normative text specifies the format, not an implementation: name the
spatialdatapackage (or another implementation) only in implementation notes and footnotes.Use the custom admonitions:
Implementation note (
implementation-note, non-normative);Divergence from NGFF (
ngff-divergence).
The pages state what is decided. Open questions go into an issue or an RFC, and candidate changes into Upcoming version (0.5).
JSON examples come from real stores; abbreviate with
...if needed.Cite as a link, for example
([Marconato et al., 2025](https://doi.org/10.1038/s41592-024-02212-x)).Short sentences, no filler. If a paragraph does not change what an implementer would do, delete it.
Building the site#
The website, including this guide, is on the branch
site, which builds every version listed in
its versions.toml into one site, and the RFCs, from the branch
rfcs, once, at its root; its README
describes how to build and preview it locally.
Licensing#
The text of the specification, of this guide and of the RFCs is licensed under CC-BY-4.0; the code (the website and the tools) under BSD-3-Clause. A contribution is made under the same licences.