Guides

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). 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).
  • Treat absent as unknown. A missing static level is not zero; a missing status means the status is unknown (absent values).
  • Show unknown vocabulary values as they are. Do not map x-jetting to "other" (recommended vocabularies).

Writing

  • Round-trip everything. Preserve x- members and any member you do not know, at every level (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).
  • Store SI, display anything. Convert to the user's units on screen and back on input. The file never changes unit (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).
  • References resolve only within the same file (cross-array references).
  • A dangling reference is a warning; never delete it silently on import or export (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).

Editing history correctly

Each top-level list is one of three kinds, and the kind decides how a correction is recorded (block kinds):

KindBlocksHow to correct
Ledgerhydrodynamic_events, production, water_samplesAppend a new entry with corrects. Editing in place is allowed but not recommended after export.
Mutable recordhistory_logs, permits, operating_regimeEdit in place and update updated_at.
Installationpump_installations, metersEdit 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). Compute them when you display or query. @welldot/utils implements the spec's derivations, including the normative ones for volumes and permit deadlines, and 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).
  • 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).

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).

Interoperability

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

Testing your implementation

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