Engineering Journal
Schema Editor
Schema Editor

Postmortem: Four Copies of the Same HTML File Became Four Different Codebases

2026-06-04

TLDR

Four domain pages (electrical, construction, software, floorplan) each had a full copy of the editor's 900-line HTML. A canvas bug fixed in the electrical copy was not applied to the floorplan copy. It was discovered three weeks later during a demo. The fix: a web component so the copies become 15-line shells.


The Assumption That Seemed Reasonable

The four domains were different enough to justify separate pages. Electrical diagrams needed electrical symbols and orthogonal wire routing. Construction layouts needed different snap grids and different export formats. Starting from one working page and copying it for each domain was the fastest path to four working pages.

The expectation was that the copies would stay "mostly the same" because the core canvas logic was unchanged. Domain customization was only in the palette and the config values.

This expectation was incorrect. The copies did not stay mostly the same. They diverged on the third day when a UI fix was applied to the electrical copy without copying it to the others.

When It Failed

Six weeks in, the floorplan domain had a canvas rendering bug: elements placed near the right edge of the viewport would snap to the wrong position after a pan. The bug was caused by an off-by-one error in the snap grid calculation.

The same bug had existed in the electrical domain three weeks earlier. It was fixed in a 10-line change to the canvas engine. The fix was committed with the message "fixed snap grid offset." The electrical domain worked correctly after that.

The floorplan copy of the canvas engine was not updated because the commit touched only the electrical file. Nobody was tracking which changes applied to all copies. The snap grid fix was electrical-specific from the commit's perspective, even though the underlying code was identical.

The bug was found by a user during a demo. The fix was applied in five minutes. The post-mortem question: what other fixes from the electrical domain had not been applied to the other three?

The answer was three fixes and one feature addition, identified by diffing the four files against each other. Two of the fixes were minor. One was a security fix to a postMessage handler.

What Was Actually Wrong

Four separate files meant four separate targets for every change. The process required developers to manually track which changes applied to all copies and apply them to each. This process was not written down, not enforced by tooling, and not part of any review checklist.

The copies diverged not through neglect but through a reasonable assumption: "this change is domain-specific, it does not need to be applied to the others." This assumption was correct for domain-specific changes and incorrect for shared canvas changes. The two types of changes were not structurally distinguishable because they lived in the same file.

What Got Deleted

The four domain-specific copies of the 900-line HTML: electrical/index.html, construction/index.html, software/index.html, floorplan/index.html. Each was replaced by a 15-line shell that imported the component and passed a domain attribute.

Also deleted: the manual sync process, which was not written down but had been followed inconsistently for six weeks.

What Replaced It

A <schema-editor> web component encapsulates all canvas logic. Domain pages are thin hosts. Every change to the canvas engine applies to all domains automatically because there is one canvas engine file. Domain-specific configuration lives in a data object inside the component. Adding a new domain requires adding a config entry and a new 15-line host page.

The first regression since the refactor: zero. The reason is structural. There is nothing to keep in sync.

The Lesson

The moment you make a second copy of a working file, you have created a sync obligation. If you do not enforce that obligation with tooling (a shared module, a component, a build system), the copies will drift. The question is not whether they will drift but how long before you notice.

Read this post in the full Engineering Journal →