Demo. Ledgerline is a made-up legacy invoicing app. Every number here is invented to
show the report.
PDF rendering runs one invoice at a time
sketchedMonth-end renders every invoice in a single
loop and waits for each PDF before starting the next. That loop is about 30 of the 38
minutes.
Plan: Pin today's PDFs with byte-for-byte snapshot tests, then render in a small
worker pool. Run it as the month-end-time hill climb.
jobs/month_end.php:212 · profile of the 3 Oct run: 31m 40s in render_invoice()
Shorten the month-end run
It blocks finance for most of an hour, and PDF rendering is most of it.
- metric
- Wall time of month-end on the 2,000-invoice fixture
- now
- 38 min
- target
- under 10 min
- area
- jobs/month-end, jobs/pdf-render
Ask your agent: /hillclimb month-end-time
Cut queries on the dashboard
The dashboard runs one query per customer. It slows as customers grow.
- metric
- SQL queries per dashboard load
- now
- 214
- target
- under 10
- area
- app/invoices
Ask your agent: /hillclimb dashboard-queries
Tax is calculated in two places
highverifiedThe
invoice screen and the PDF compute tax separately. They round differently on mixed-rate
invoices.
app/Invoice/Totals.php:40 · jobs/pdf/Totals.php:18
Dashboard runs one query per customer
medsketchedA loop
loads each customer's balance on its own.
app/Dashboard/Balances.php:61 · 214 queries on the demo data
The utils file is a junction
medverified38 files
import it, and it mixes dates, money and HTML helpers.
shared/lib/utils.php · 1,840 lines
Import job has no tests
lowunverifiedIt ran
last in March. It may be dead.
jobs/imports · last change 7 months ago
Why invoices lock at 23:00
The old bank export ran at 23:30 and read the invoices table directly. Locking at 23:00
kept totals still while it ran. The export moved to the API in 2023, so the lock may no
longer be needed.
From: engineer, session 4 · git blame on month_end.php
Two admins
The new admin was built beside the old one to avoid a big-bang switch. Finance still uses
the old one for credit notes.
From: engineer, session 2
Size × recent changes. A small share of files usually holds most of the risk.