All writing01 / 11JavaScript

Two Copies of One Fact

My first production code was Laravel and jQuery, and it taught me a rule I keep finding everywhere: most interface bugs are two copies of one fact that stopped agreeing.

Zahin Mohammad

Software Developer

Published
Reading time
4 min read
Length
680 words
Topics
JavaScript, State, Fundamentals

The first production code I wrote was Laravel on the server and jQuery in the browser. It’s an unfashionable way to start in 2023, and I’m glad it was mine, because jQuery hides almost nothing. When something on the page is wrong, there is no framework to blame. There is only the code you wrote to change the page, and the moment you forgot to.

One Bug in Many Costumes

If you’ve written jQuery, you’ve met this bug in some costume. A cart badge says three items while the cart holds two. A Save button stays disabled after the form becomes valid again. A filter panel says “All” while the list underneath is filtered. A row deleted on the server still sits in the table.

They look like four problems. They’re one. In every case a single fact about the world (how many items are in the cart, whether the form is valid, which filter is active) is stored in two places: once in a JavaScript variable or on the server, and once in the DOM. Some code updates the first copy. Different code is supposed to update the second. Sooner or later a path through the program updates one and not the other, and the page starts telling a small lie.

cart.js
let count = 0;

$("#add").on("click", () => {
  count += 1;
  $("#badge").text(count);
});

$("#clear").on("click", () => {
  count = 0;
  // The badge still says the old number. The page now disagrees with itself.
});

Count the Copies

The useful habit is to count the copies. When a bug like this appears, ask where the fact lives and how many places think they know it. If the answer is more than one, the bug is almost always in the gap between them, and the fix is rarely one more update call. That patches one path. The next path nobody thought of finds the gap again.

The real fix is to make one copy the source and the other a consequence. In jQuery that means one function that draws the badge from the count, called after every change, instead of letting each handler edit the DOM its own way:

cart.js
let count = 0;

function render() {
  $("#badge").text(count).toggle(count > 0);
}

$("#add").on("click", () => {
  count += 1;
  render();
});

$("#clear").on("click", () => {
  count = 0;
  render();
});

It looks like a small refactor. It’s actually a change of model. The DOM stops being a place where facts live and becomes a picture of facts that live somewhere else.

Why the Frameworks Exist

As far as I can tell, this is the core idea behind React and Vue. “UI is a function of state” sounds like a slogan until you’ve spent a few months keeping two copies of one fact in sync by hand. Then it reads like a description of the exact pain you have, with a framework built around the cure. You describe what the page should look like for a given state, and the framework takes a new photograph every time the state changes.

There’s an argument here for learning the old way first, briefly. Not because jQuery is better, but because a framework is an answer, and answers make far more sense once you’ve asked the question yourself.

The Rule Outside the Browser

The rule doesn’t stay in the browser. A cache is a second copy of a database row. A total column is a second copy of the sum of its line items. Documentation is a second copy of what the code does, which is exactly why it drifts. A spreadsheet someone keeps “just for reporting” is a second copy of the whole system.

Not every second copy is a mistake. Caches exist because reading the source is slow, and stored totals exist because computing them is expensive. The point isn’t to never duplicate a fact. It’s to know, for every duplicate, which copy is the truth and what keeps the other one honest. A copy that can’t answer both questions is a bug that hasn’t happened yet.

None of this came from a tutorial. It came from noticing that a whole family of bugs has one shape, and that once you can see the shape, you stop fixing the bugs one at a time and start removing the place they come from.

Next post02 / 11Software Has Desire PathsPlanners pave the paths they expect people to walk. People wear their own into the grass. Testing is the work of finding those paths before users do.

Open to select freelance work

Have an IdeaWorth Building?

Building a product, or hiring for one? I’d like to hear about it.

Based in Chittagong, Bangladesh. It’s here (GMT+6).