# Building an editor or database on .well

> Design decisions for software that reads and writes .well files at scale, such as editors, registries, databases and data pipelines, with links to the rules behind each.

This guide is for developers building a second editor, a well registry, a database import or a data pipeline on the format. It lists the decisions that are easy to get wrong and the spec rules that settle them.

## Reading

- **Accept every version you can.** Read `1`, `2` and `"2.x"`; upgrade v1 on read; reject malformed and unknown major versions with a clear message ([parser version handling](/format/spec/v2/format-reference#parser-version-handling)). `parseWell` in `@welldot/core` does all of this.
- **Do not assume order.** Events, samples and log entries may appear in any order. Sort by instant (as absolute UTC time) and use `sequence` to break ties ([ordering and ties](/format/spec/v2/format-reference#ordering-and-ties)).
- **Treat absent as unknown.** A missing static level is not zero; a missing status means the status is unknown ([absent values](/format/spec/v2/format-reference#absent-values-since-v24)).
- **Show unknown vocabulary values as they are.** Do not map `x-jetting` to "other" ([recommended vocabularies](/format/spec/v2/format-reference#recommended-vocabularies)).

## Writing

- **Round-trip everything.** Preserve `x-` members and any member you do not know, at every level ([design principles](/format/spec/v2/overview#design-principles), "Preserve unknown members"). The safest pattern is to parse into an object, change the fields you edit, and serialize the same object.
- **Write the current version.** New files are `"2.4"`. Upgrading `2` to `"2.4"` is safe; `serializeWell` writes it.
- **Omit, do not null.** Remove optional fields that have no value ([absent values](/format/spec/v2/format-reference#absent-values-since-v24)).
- **Store SI, display anything.** Convert to the user's units on screen and back on input. The file never changes unit ([Units](/format/spec/v2/format-reference#units)).
- **Store keys, not labels.** Write `stainless_steel`, show "Stainless steel" or "Aço inox".

## Ids and references

- Ids are strings, unique per block; generate UUID v4 for new records ([`id` field convention](/format/spec/v2/format-reference#id-field-convention)).
- References resolve only within the same file ([cross-array references](/format/spec/v2/format-reference#cross-array-references)).
- A dangling reference is a warning; never delete it silently on import or export ([dangling references](/format/spec/v2/format-reference#dangling-references)).
- Duplicate ids are malformed. An interactive editor asks the user to merge, rename or abort; a batch importer rejects the file. Never rename automatically ([duplicate ids](/format/spec/v2/format-reference#duplicate-ids-within-a-scope)).

## Editing history correctly

Each top-level list is one of three kinds, and the kind decides how a correction is recorded ([block kinds](/format/spec/v2/format-reference#block-kinds-since-v23)):

| Kind           | Blocks                                               | How to correct                                                                                    |
| -------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Ledger         | `hydrodynamic_events`, `production`, `water_samples` | Append a new entry with `corrects`. Editing in place is allowed but not recommended after export. |
| Mutable record | `history_logs`, `permits`, `operating_regime`        | Edit in place and update `updated_at`.                                                            |
| Installation   | `pump_installations`, `meters`                       | Edit in place; a reinstalled unit is a new entry with the same serial.                            |

Construction and geology are edited in place. An `aquifer_analysis` is an interpretation: a new interpretation is a new entry, and earlier ones stay.

## Derived values

Never store what can be computed: drawdown, specific capacity from readings, production volumes, permit status, condition deadlines, exceedances ([design principles](/format/spec/v2/overview#design-principles)). Compute them when you display or query. [`@welldot/utils`](/utils) implements the spec's derivations, including the normative ones for [volumes](/format/spec/v2/object-schemas#volume-derivation-normative) and [permit deadlines](/format/spec/v2/object-schemas#deadline-generation-normative), and [Querying current state](/format/spec/v2/object-schemas#querying-current-state) describes how to answer "what is the well's status now".

## Databases

A `.well` file is a document, and the simplest durable storage is the document itself: a JSON column, a document store or files in object storage. Index what you query (identifiers, location, depth, status) in separate columns, derived from the document on write.

When you normalize into tables:

- Key a well by its `well_id` entries (authority plus id), not by `name`; the pair is unique within a file ([`well_id`](/format/spec/v2/format-reference#well_id)).
- Key child records by the well plus their `id`; ids are only unique within one block of one file.
- Keep the original document, or at least its `x-` members and unknown fields, so you can export a faithful file again.
- Collective permits and campaign blanks are repeated in each well's file with the same id; you may merge them by id ([repeated records](/format/spec/v2/format-reference#repeated-records-since-v24)).

## Sharing and privacy

To publish a well without its owner data, permits or location, remove those parts rather than blanking them, so the result is still valid. `redactWell` in `@welldot/core` does this by section ([Serializing and sharing](/core/guides/serializing-and-sharing)).

## Interoperability

- GroundWater ML 2 (GWML2) is the OGC standard for groundwater data; the spec maps `.well` objects to its classes ([relationship to GWML2](/format/spec/v2/interoperability#relationship-to-gwml2)).
- `@context` is reserved for a JSON-LD context; it is optional and readers must accept it ([JSON-LD context](/format/spec/v2/interoperability#json-ld-context-reserved)).
- The MIME type is `application/vnd.well+json` and the extension `.well` ([overview](/format/spec/v2/overview)).

## Testing your implementation

Run your reader and writer against all 19 [example wells](/format/reference/examples): every one must parse, and writing it back must keep every member. Include the [inconsistent data](/format/reference/examples) and [incomplete record](/format/reference/examples) examples, which are valid on purpose.
