Motivation
Reading this codebase costs more than it should. A large share of variables are named for their
position in a loop or their first letter rather than for what they hold — a single letter, a stray
abbreviation, or a generic word that could mean five different things depending on the function you
happen to be in. The same concept picks up a different name in every file that touches it.
The cost lands on everyone who is not the original author. New contributors spend their first weeks
tracing assignments backwards to work out what a value actually is. Reviewers cannot tell from a
diff whether a change is correct, because the identifiers do not say what the code is operating on.
AI coding assistants — increasingly how work gets done here — mis-infer intent from meaningless
names and produce plausible but wrong edits. Bugs hide in this: two different things sharing a
vague name is exactly the shape of a mix-up nobody notices in review.
This is also foundational to where the project is going. The plugin framework direction depends on
a core whose boundaries and contracts are legible to outside plugin authors. A core nobody can read
is a core nobody can build against.
How it should work
The work happens in two passes, both landing together.
First, an inventory. Every unclear identifier in scope gets collected into a single reviewable
list: the current name, where it lives, what it actually holds once you read the surrounding code,
and the proposed replacement. This list is the artifact a reviewer checks — it is where someone can
disagree with a specific rename before any code moves, and it is what makes a large diff auditable.
Names are grouped by the concept they represent, so the same idea gets the same new name everywhere
rather than three near-synonyms.
Second, the renames. The proposed names get applied. A reader should be able to open any file in
scope and understand what a value is from its name alone, without scrolling up to the assignment or
stepping through in a debugger. Where the same concept appears in more than one place, it reads the
same way in all of them.
Alongside the renames, the conventions the new names follow get written down — what a value holding
a layer configuration is called versus one holding a rendered layer instance, when an abbreviation
is acceptable, how loop and index variables are handled — so the next person adding code extends
the convention instead of inventing a new one.
Behavior does not change. This is a pure readability pass: same inputs, same outputs, same rendering,
same API responses. Anything that would alter behavior is not part of this work.
Done when
Out of scope
- Any behavior change. No bug fixes, no refactors that move logic, no signature changes beyond
the parameter names themselves. If a bug is spotted along the way, file it separately.
- The short global module aliases. The handful of terse names the whole codebase imports are
deliberately left alone here — they are a convention of their own, touch a very large number of
files, and deserve a separate decision and a separate pass.
- The Configure admin interface. Out of scope for this pass.
- Anything externally visible. Names that appear in stored configurations, database columns,
request and response payloads, URL parameters, or the public JavaScript API stay exactly as they
are, even when they are poorly named. Renaming those breaks existing deployments and saved links;
they need their own migration-aware plan.
- Restructuring files or modules. No moving code between files, no splitting large files.
Draft implementation plan — written as of ad6ba547 on 2026-07-29. Rough guide; re-verify against latest code.
Current behavior
Scope is src/essence (~189 files, ~94k lines) and API/ (~61 files, ~14.5k lines).
A quick survey of declared identifiers shows the shape of the problem. In src/essence, the most
frequently declared names are i (296), layer (75), d (66), c (64), l (57), s (50),
j (42), p (41), x (39), v (36), f (34), b (25), a (22). In API/, i (57), f (10),
q (8), r (6), j (6).
Density is worst in:
src/essence/Basics/Formulae_/Formulae_.js — 133 one/two-character declarations
src/essence/Tools/Draw/DrawTool_Editing.js — 79
src/essence/Tools/Draw/DrawTool_Drawing.js — 65
src/essence/Basics/Layers_/Layers_.js — 49
src/essence/Basics/Map_/Map_.js — 37
src/essence/Tools/Draw/DrawTool_Files.js — 35
src/essence/Tools/Viewshed/*, src/essence/Tools/Shade/* — ~28-31 each
Representative example: in Layers_.js, s is a persisted layer configuration object — reads like
s.kind, s.visibilitycutoff, s.name. Nearby, layerObj is the same kind of thing under a
different name, and l is sometimes the config and sometimes the constructed Leaflet layer. That
config-vs-instance ambiguity is the single highest-value distinction to fix and is a good seed for
the naming convention.
Formulae_.js is a different case — much of it is math (dx/dy/dz, sx/cx, a/c in a
haversine). Short names that match standard mathematical notation are fine; the fix there is a brief
comment naming the formula, not renaming dx to deltaX.
Where the change lands & rough plan
- Build the inventory first. Sweep both trees, collect unclear identifiers with file, current
name, inferred meaning, and proposed name. Group by concept so one concept gets one name. Commit
this as a document (e.g. under docs/) as the first commit — it is what a reviewer actually reads.
- Agree the vocabulary for the recurring concepts before touching code: layer configuration vs.
constructed layer instance vs. layer display name vs. layer UUID; feature vs. geometry vs. shape;
coordinate vs. point vs. position. Write it into the conventions note.
- Apply renames file by file, one commit per file or per tool, so the history stays bisectable
and a reviewer can take it in pieces. Prefer editor/LSP-assisted rename over find-and-replace.
- Verify continuously —
npm run typecheck, npm run test:unit, npm run test:e2e, plus the
manual flow pass listed in Done when.
⚠️ Gotcha: there is almost no test coverage to catch a bad rename. Only two unit test files
exist (src/App.test.js, src/essence/Tools/Draw/DrawTool.test.js), and npm run typecheck only
covers the partially-migrated TypeScript files — the bulk of src/essence is plain JavaScript with
no static checking at all. A mis-scoped rename in a .js file fails silently at runtime. This is
why the manual flow pass is a hard acceptance item and not a nicety.
⚠️ Gotcha: property names on layer configuration objects are a persisted schema, not local
names. Keys like kind and visibilitycutoff come from mission configuration stored in the
database and are read by the Configure admin interface. Renaming the variable holding the object
is safe; renaming a property on it silently breaks every existing mission. The same trap applies
to fields crossing the frontend/backend boundary in request and response payloads, and to URL
parameter names used by the tools that serialize state into permalinks (~11 call sites).
⚠️ Gotcha: this codebase couples identifiers to DOM element ids, jQuery selectors, and CSS class
names in places. A rename that also touches a selector string breaks styling or event binding
with no error at all. Grep the string form of any name before renaming it.
⚠️ Gotcha: a sweep this wide conflicts with everything in flight. Coordinate with open branches
and land it in a quiet window, or it will be rebased forever.
References
Starting points, snapshot-accurate only:
src/essence/Basics/Layers_/Layers_.js, src/essence/Basics/Map_/Map_.js — core, highest leverage
src/essence/Basics/Formulae_/Formulae_.js — densest, but much of it is legitimate math notation
src/essence/Tools/Draw/ — largest cluster of unclear names in one tool
src/essence/Tools/Viewshed/, src/essence/Tools/Shade/ — algorithm-heavy, same math caveat
API/Backend/APIs/ — backend route handlers
package.json scripts: typecheck, test:unit, test:e2e
Motivation
Reading this codebase costs more than it should. A large share of variables are named for their
position in a loop or their first letter rather than for what they hold — a single letter, a stray
abbreviation, or a generic word that could mean five different things depending on the function you
happen to be in. The same concept picks up a different name in every file that touches it.
The cost lands on everyone who is not the original author. New contributors spend their first weeks
tracing assignments backwards to work out what a value actually is. Reviewers cannot tell from a
diff whether a change is correct, because the identifiers do not say what the code is operating on.
AI coding assistants — increasingly how work gets done here — mis-infer intent from meaningless
names and produce plausible but wrong edits. Bugs hide in this: two different things sharing a
vague name is exactly the shape of a mix-up nobody notices in review.
This is also foundational to where the project is going. The plugin framework direction depends on
a core whose boundaries and contracts are legible to outside plugin authors. A core nobody can read
is a core nobody can build against.
How it should work
The work happens in two passes, both landing together.
First, an inventory. Every unclear identifier in scope gets collected into a single reviewable
list: the current name, where it lives, what it actually holds once you read the surrounding code,
and the proposed replacement. This list is the artifact a reviewer checks — it is where someone can
disagree with a specific rename before any code moves, and it is what makes a large diff auditable.
Names are grouped by the concept they represent, so the same idea gets the same new name everywhere
rather than three near-synonyms.
Second, the renames. The proposed names get applied. A reader should be able to open any file in
scope and understand what a value is from its name alone, without scrolling up to the assignment or
stepping through in a debugger. Where the same concept appears in more than one place, it reads the
same way in all of them.
Alongside the renames, the conventions the new names follow get written down — what a value holding
a layer configuration is called versus one holding a rendered layer instance, when an abbreviation
is acceptable, how loop and index variables are handled — so the next person adding code extends
the convention instead of inventing a new one.
Behavior does not change. This is a pure readability pass: same inputs, same outputs, same rendering,
same API responses. Anything that would alter behavior is not part of this work.
Done when
reviewable on its own without reading the diff.
what each value is without tracing it back to its assignment.
styling layers, drawing and editing shapes, running the analysis tools, and the 3D globe view.
database record needs editing as a result of this work.
Out of scope
the parameter names themselves. If a bug is spotted along the way, file it separately.
deliberately left alone here — they are a convention of their own, touch a very large number of
files, and deserve a separate decision and a separate pass.
request and response payloads, URL parameters, or the public JavaScript API stay exactly as they
are, even when they are poorly named. Renaming those breaks existing deployments and saved links;
they need their own migration-aware plan.
Draft implementation plan — written as of
ad6ba547on 2026-07-29. Rough guide; re-verify against latest code.Current behavior
Scope is
src/essence(~189 files, ~94k lines) andAPI/(~61 files, ~14.5k lines).A quick survey of declared identifiers shows the shape of the problem. In
src/essence, the mostfrequently declared names are
i(296),layer(75),d(66),c(64),l(57),s(50),j(42),p(41),x(39),v(36),f(34),b(25),a(22). InAPI/,i(57),f(10),q(8),r(6),j(6).Density is worst in:
src/essence/Basics/Formulae_/Formulae_.js— 133 one/two-character declarationssrc/essence/Tools/Draw/DrawTool_Editing.js— 79src/essence/Tools/Draw/DrawTool_Drawing.js— 65src/essence/Basics/Layers_/Layers_.js— 49src/essence/Basics/Map_/Map_.js— 37src/essence/Tools/Draw/DrawTool_Files.js— 35src/essence/Tools/Viewshed/*,src/essence/Tools/Shade/*— ~28-31 eachRepresentative example: in
Layers_.js,sis a persisted layer configuration object — reads likes.kind,s.visibilitycutoff,s.name. Nearby,layerObjis the same kind of thing under adifferent name, and
lis sometimes the config and sometimes the constructed Leaflet layer. Thatconfig-vs-instance ambiguity is the single highest-value distinction to fix and is a good seed for
the naming convention.
Formulae_.jsis a different case — much of it is math (dx/dy/dz,sx/cx,a/cin ahaversine). Short names that match standard mathematical notation are fine; the fix there is a brief
comment naming the formula, not renaming
dxtodeltaX.Where the change lands & rough plan
name, inferred meaning, and proposed name. Group by concept so one concept gets one name. Commit
this as a document (e.g. under
docs/) as the first commit — it is what a reviewer actually reads.constructed layer instance vs. layer display name vs. layer UUID; feature vs. geometry vs. shape;
coordinate vs. point vs. position. Write it into the conventions note.
and a reviewer can take it in pieces. Prefer editor/LSP-assisted rename over find-and-replace.
npm run typecheck,npm run test:unit,npm run test:e2e, plus themanual flow pass listed in Done when.
References
Starting points, snapshot-accurate only:
src/essence/Basics/Layers_/Layers_.js,src/essence/Basics/Map_/Map_.js— core, highest leveragesrc/essence/Basics/Formulae_/Formulae_.js— densest, but much of it is legitimate math notationsrc/essence/Tools/Draw/— largest cluster of unclear names in one toolsrc/essence/Tools/Viewshed/,src/essence/Tools/Shade/— algorithm-heavy, same math caveatAPI/Backend/APIs/— backend route handlerspackage.jsonscripts:typecheck,test:unit,test:e2e