Lab / Open methodology

Frontend optimization: a 50,000-invoice list

50,000 invoices filtered and listed: 1.39 s → 16.5 ms. Synthetic lab; median of 10 runs. Open code and raw data.

CompletedSMBEnterprisePartners

Render every matching rowtoRender the visible window

Web arayüzü / Web UI LAB MEASUREMENT

50,000 invoices. Render the rows the screen needs.

Filter overdue invoices. Identical records and scrollable range; a different number of rows rendered at once.

Business effect

The finance team spends less time waiting for a large list to respond. Keep the complete result set and render the visible section.

BEFORERender every matching row
MODERNIZEDRender the visible window
Source invoice records50,000
1,000Recorded loads only50,000
Actual time scale
SAME TASK · SAME DATA10 runs · median
01

Render every matching row

A separate DOM row for every match.

1.386 s
Invoice rows in the DOM
33,333
02

Render the visible window

Complete result list; a 40-row viewport window.

16.5 ms
Invoice rows in the DOM
40

Bars start at zero and share the same time scale across implementations.

Completion time reduced98.8%Time difference: 1.369 sOriginal → modernized implementation
What changed?

Replace one DOM row per match with a 40-row viewport window. The same filter still examines every record; scrolling updates the window.

All load points and measurements
1,000 · Source invoice records
ImplementationMedian durationInvoice rows in the DOM
Render every matching row29.4 ms666
Render the visible window16.6 ms40
10,000 · Source invoice records
ImplementationMedian durationInvoice rows in the DOM
Render every matching row244.9 ms6,666
Render the visible window16.55 ms40
50,000 · Source invoice records
ImplementationMedian durationInvoice rows in the DOM
Render every matching row1.386 s33,333
Render the visible window16.5 ms40
Customer names stay text.
  • Customer 42

    Before

    Text preserved

    ✓ After

    Text preserved; no HTML element created

  • A & B

    Before

    Text preserved

    ✓ After

    Text preserved; no HTML element created

  • North < East > West

    Before

    Text preserved

    ✓ After

    Text preserved; no HTML element created

  • Quote " and apostrophe '

    Before

    Text preserved

    ✓ After

    Text preserved; no HTML element created

  • <b data-probe>Customer 42</b>

    Before

    HTML element created

    ✓ After

    Text preserved; no HTML element created

  • <span title="synthetic">Invoice</span>

    Before

    HTML element created

    ✓ After

    Text preserved; no HTML element created

Security test and evidence ↗

Try it yourself

Replay the recorded lab results side by side.

As the workload grows

Duration by workload

  • Before
  • After

Duration by workload (ms)

05001,0001,5001,00010,00050,0001,385.8 ms16.5 ms

Select a data point

05001,0001,5001,00050,0001,385.8 ms16.5 ms

Select a data point

Source invoice recordsMarkers: measured points
Show as a table
Duration by workload
Source invoice recordsBeforeAfter
1,00029.4 ms16.6 ms
10,000244.9 ms16.55 ms
50,0001,385.8 ms16.5 ms

How we measured

Environment, protocol and technical analysis

Environment and data

Environment
Chrome 154 · macOS · 8 logical cores · viewport 1280 × 720
Dataset
1,000 / 10,000 / 50,000 synthetic invoices; same overdue filter and complete result checksum.
Tools
performance.now · requestAnimationFrame · DOM / scroll checks · Python independent result oracle
Repetitions
3 sizes × 2 implementations × 10 recorded runs + 12 warmups; every run retained.
Open raw measurement data ↗

50,000 invoices. Render the rows the screen needs.

Filter overdue invoices. Identical records and scrollable range; a different number of rows rendered at once.

Alternating implementations in a visible browser tab. Two warmups followed by 10 runs per implementation and size. Verify the complete matching result checksum, top/bottom viewport and scroll height. Timing starts before filtering and includes DOM, layout and two requestAnimationFrame callbacks; it does not instrument physical screen presentation. Primary point: 50,000 source invoices.

Inspect the measurement script ↗

What changes for the business?

The finance team spends less time waiting for a large list to respond. Keep the complete result set and render the visible section.

Change and scope

Replace one DOM row per match with a 40-row viewport window. The same filter still examines every record; scrolling updates the window.

Synthetic invoices; Chrome 154 on macOS at 1280 × 720. Measures filtering, DOM update, layout and two animation-frame callbacks. Excludes network, database and field INP.

Rendering fewer rows still examines all the data

33,333 of the 50,000 invoices match the filter. Both implementations find the complete set. The updated screen keeps only 40 DOM rows and also verifies scrolling to the bottom. The slowest modern run, 112.7 ms, remains in the raw evidence.

The text and HTML boundary

All six harmless encoding checks preserved input as text with textContent. The old HTML concatenation created elements for two inputs. This tests data rendering, not every XSS vector or the security of an entire application.

Where to start in your system

Identify the slow operation, data volume and peak workload. Establish its baseline, change the code and access pattern, then compare the same result.

What we did

  1. Business rule and data

    AIPrepared the synthetic workflow, tests and measurement automation.

    VerificationPublished code and raw outputs are available for review; this is not a client project.

  2. Implementation change

    AIReplace one DOM row per match with a 40-row viewport window. The same filter still examines every record; scrolling updates the window.

    VerificationOutput equivalence was independently checked; every repetition is retained.