Jertype

Member of Information Superhighway

Code removal debugging

Sometimes an app is broken and I’m not sure what to do next.

It’s loading a blank screen, there’s errors in the console, or its running very slow.

The setup

Imagine a React application that displays a list of customers, staff, and transactions from the current day for a shop.

The transaction list has hundreds of items because your shop is pretty popular. It sells gopher themed dumplings.

<Customers />
<Staff />
<Transactions />

The application loads slowly but you aren’t sure what code commit caused it.1

Delete stuff that might be the problem

In this situation I delete or comment out code until the performance improves or the error goes away.

I might not understand everything I’m removing or parts of the app might not work. It’s okay, because this is a strategy to get unstuck.

<!-- <Customers customers={customers} /> -->
<!-- <Staff staff={staff} /> -->
<Transactions transactions={transactions} />

I comment out the Customers and Staff components and the app is still performing slowly.

Add it back if it’s not

We now know the problem isn’t there and we can ignore those components. What if I comment out Transactions?

<Customers customers={customers} />
<Staff staff={staff} />
<!-- <Transactions transactions={transactions} /> -->

Now the application is performing normally but the transactions are not displayed.

We’ve narrowed down the problem to the Transactions component so let’s uncomment it and look inside it.

const Transactions = (transactions) => {
	const formattedTransactions = transactions.map(t => formatTransaction(t));

	return (
		<ul>
			{
				formattedTransactions.map(t => (
					<li key={t.id}>{t.id} | {t.time}</li>
				));
			}
		</ul>
	)
}

const formatTransaction(transaction)
{
	return {
		id: t.id,
		time: Intl.DateTimeFormat("en-US", {year: "numeric", month: "numeric", day: "2-digit"}).format()
	};
}

Hardcode

It looks good to me but let’s replace formattedTransactions with a hardcoded result to see if that function is the problem.

const Transactions = (transactions) => {
	// const formattedTransactions = transactions.map(t => formatTransaction(t));

	// Generate an array of 100 elements then create transactions from that array
	const range = Array.from({length: 100}, (x, i) => i);
	const formattedTransactions = range.map(i => {id: i, time: new Date('2023-04-01')});

	return (
		<ul>
			{
				formattedTransactions.map(t => (
					<li>{t.id} | {t.time}</li>
				));
			}
		</ul>
	)
}

After hardcoding the result, the performance is still good. Now we know the problem is probably in the formatTransaction(t) call.

Look closer

It turns out, the way time is formatted is causing an issue:

const formatTransaction(transaction)
{
	return {
		id: transaction.id,
		time: Intl.DateTimeFormat("en-US", {year: "numeric", month: "numeric", day: "2-digit"}).format(transaction.date)
	};
}

Each time the transaction’s time is formatted it uses a new instance of the Intl.DateTimeFormat object. This is ok with a few transactions but with a hundred it takes long enough for the page to feel slow.

We can address this by creating a single Intl.DateTimeFormat object and reusing it.

const dtf = Intl.DateTimeFormat("en-US", {year: "numeric", month: "numeric", day: "2-digit"});

const formatTransaction(transaction)
{
	return {
		id: transaction.id,
		time: dtf.format(transaction.date)
	};
}

And now our app is performing as expected. It can be easy to miss this sort of thing. But deleting code so we can figure out what can be safely ignored can help narrow down the problem.

Recap

There are more sophisticated ways to tackle this problem using profiling tools. However, sometimes it’s helpful to have a basic strategy like this.


  1. You could use an Application Performance Monitoring tool like sentry or newrelic to help with this. ↩︎