---
title: "The page was performing a ceremony, not a job"
canonical: https://dxdev.com/blog/2026-04-08_overview-page-database-ritual/
datePublished: 2026-04-08
---
The marketing overview page in HTO's admin had over 80 blocking SQL queries on a single load. I opened ITEM-6767 expecting to find one bad query to tune.

That instinct was wrong: the page had no single slow query, it had a blocking posture that nobody had named as a problem.

## What the page was actually doing

HTO is a 25-year-old ASP Classic application. The marketing overview exists for internal ops, a dashboard of product performance, session counts, and summary metrics across the customer base. It looked normal, and had always looked normal. It was just slow.

When I traced the execution path, the page was built on an implicit contract: the server would finish all its work before the browser got anything. Every panel queued behind the one before it. SQL ran serially, one recordset closed before the next one opened. The ribbon section, the product table, the session counts, the PPC numbers, all of it waited in line. The page didn't have a slow query; it had a posture. Blocking was the posture.

That number didn't happen by design; it accumulated. One quarter someone adds a product breakdown. The next year someone adds session stats. Nobody asks how the page performs with each addition, because the page still loads. Slowly, but it loads, and everyone gets used to waiting. The slowness becomes its personality.

## The wrong diagnosis

The first instinct on a slow page is to find the offending query: a missing index, a cross join, a subquery that should be a join. Fix the worst one, rerun, measure. I've followed this instinct on other pages in this codebase, and it's often right.

It would have been the wrong move here. The slowness wasn't concentrated in one place; it was distributed across a contract nobody had challenged. Even if I'd cut the worst query in half, I'd have addressed maybe 1% of the total load, because the architecture itself was the problem.

## Ribbon first, everything else later

The real fix was to throw out the page contract.

I kept only the ribbon data on the initial server response. The ribbon is what renders in the first visible moment: a summary strip across the top. Everything else, the product panels, the session counts, the auxiliary metrics, gets loaded afterward via parallel ajax calls to `MarketingProduct.asp`, which I rewired to return JSON instead of rendered HTML.

On the client side, I rebuilt `Marketing.js` so each section renders independently when its data arrives. There's no global wait: the ribbon appears immediately, and the rest fills in as responses come back, each section on its own schedule. If one section is slow, it does not hold any other section hostage.

The page now does less work on the initial load than it used to do for any single section.

## Deletion with receipts

Performance work on legacy code is mostly deletion. The question to ask is not "how do I make this faster" but "is this work still load-bearing."

Two things were not load-bearing.

First: the PPC computation. It was server-side work that ran on every load and fed data into a section that, when I actually looked, was not wired up to display anything. The computation was real, but the consumer had been gone for who knows how long. I cut it.

Second: the `HTOsessions` queries. The page was running them twice: once in the ribbon pass and once in the main body. Same query, same parameters, same recordset, two trips to SQL. The second one was a copy that had drifted out of sync with where the data was actually used. I kept one and removed the other.

Neither of these was the bottleneck. But dead work is still work, and in a page with over 80 queries, dead work is how you get to that number in the first place.

One question I didn't fully answer: how many other admin pages in HTO are in the same shape. The session reports, billing summaries, and reporting views all load. I haven't traced their query counts, and the marketing overview felt normal before I traced it too. The only way to tell the difference is to count.

## The ribbon styling fix was the useful proof

After the architecture change went in, a small thing happened: I needed to fix a styling issue on the ribbon. A spacing adjustment, something cosmetic.

On the old page, a change to the ribbon would have required touching the same server-side template that controlled everything else. Any edit was a risk to the whole load path. I'd have read the surrounding code more carefully than the fix warranted, just to make sure I wasn't breaking something downstream.

On the new page, the ribbon is isolated. I opened the relevant section, made the adjustment, confirmed it in the browser, and the rest of the page was not a concern.

The proof wasn't a benchmark. It was a ribbon spacing fix that opened `Marketing.js` and closed without `MarketingProduct.asp` ever mattering.
