The problem
The desktop app took more than two minutes to load. I had just become the only engineer on the codebase. It had been reported in tickets since 2023, most recently rated low priority, and an earlier change in May 2025 had loaded the most important data first without fixing the whole start-up. I treated it as critical and put it ahead of further feature work.
What I measured
User reports in the tickets put the wait at about 120 to 130 seconds, and longer after a crash or a night offline. Those were reports, not scripted runs. I measured before changing anything, using browser developer tools, backend logs and timing in the code. That showed five causes working together:
- It fetched more data than the first screen needed.
- Several loads ran one after another instead of together.
- A local sync cap of 200,000 records was far above what the app needed.
- The backend scanned whole tables and looked users up one at a time.
- Bulk test data from 2023 had left 64,805 soft-deleted rows in one table.
What I changed
Each cause got its own fix: scoped parallel fetching, a start-up gate, lazy service start, pagination, indexed queries with a user cache, and a purge of the soft-deleted rows. Deleting data is the step that cannot be undone, so I wrote the plan down and the product owner reviewed it before it ran. I also added performance spans, dashboards and alarms so a regression would show.
The result
I measured the packaged production app (version 1.18.4) with a script that records the time from launch to the first page of the home list. Five cold launches had a median of 1.6 seconds (range 1.2 to 2.2). Five warm reloads had a median of 0.7 seconds (range 0.7 to 0.8). No run was dropped.
This is a small check: one account of about 600 items, one laptop, one network. Cold times drift down across the five runs, which fits the operating system warming its file cache, so the first launch after a restart will be slower. The original “over two minutes” was never scripted and has no run count, so I do not quote a ratio.
The purge ran in production after the product owner reviewed the scope. A later read-only audit of every table found about 4 soft-deleted rows across 32 production tables, so the data cause has not come back.
Later: the shared lists
Months later the “shared with me” lists took about 3.5 seconds, and the database was the obvious suspect. A scripted request through the real API showed the time was not spent waiting on data. The function was starved of CPU at its 256 MB memory setting, and on AWS Lambda CPU scales with memory. Raising it to 1,024 MB accounted for most of the gain. A second change made several functions look up only the users a request needed, not the whole user pool.
On development, across 34 queries with 8 to 9 calls each, the median for the three largest lists fell from about 3.5 seconds to about 0.33 seconds. Smaller lists improved 1.4 to 2.6 times and two did not change. A small check on production, with one test account and 12 list calls, gave warm medians of 0.27 to 0.56 seconds and first calls of about 2 seconds. That is a small check, not a load test.
What I would do differently
Keep a small scripted timing run from day one, so every claim has raw numbers behind it. I do that now for all performance work.