When Custom Code Goes Quietly Wrong

A midsize food producer called us about their sales order screen. It was slow. That was the whole complaint.

I sat with someone on the sales team, a guy who’d been there for years, and asked how he handled the wait. He looked at me for a second like the question didn’t quite land. Then he explained, without any real complaint in his manner, that he’d open the order, walk to the coffee machine, come back, and by then it was usually ready. He had the timing worked out.

When I pushed on it, he almost defended it. It worked for him. The whole team had a version of the same routine.

That’s the part of these calls that stays with me. Not the problem itself. The way people build small rituals around a system’s failings until the failings feel like the system.

Most of the performance problems we walk into look like this. They weren’t there at go-live. They built up over years, one small custom piece at a time, until they became the way things worked.

The customizations themselves weren’t mistakes. They solved real problems back when they were written. But nobody looked at them again. And the way systems fail when nobody’s looking isn’t dramatic. It’s slow, patient, and easy to live with.

Here are two we’ve seen recently: different symptoms, same underlying pattern.

Slow sales order screen:

When we opened up what was actually happening under that screen, the answer wasn’t hard to find. A custom field on the sales order was showing live stock availability. It had been built years ago, back when the company was still on NAV, by someone who wanted the number visible without having to click through to another screen. Good idea.

The way it was written was the problem. Every time the screen loaded, it walked through the item ledger, entry by entry, and added up the movements for that item to determine what was on the shelf right now. Every posted movement, ever, going back to the day they went live on NAV. Fine on day one, when the ledger had a few thousand entries. Over a decade in, with the ledger millions of entries deep, every screen load was doing that same walk. Per user, all day.

Nobody who knew the platform properly would have written it that way, even at the time. NAV had a better way of doing this from the start. But the person who did wasn’t wrong to try. They needed the number on the screen, and this was the way they knew. Once it was in, nobody had a reason to open it up again.

The fix was small. We rewrote the field so it used the platform’s own maintained totals (a SIFT lookup, in BC terms) instead of recalculating from history each time. Same value on the screen. A rounding error in the work behind it.

We deployed it on a Thursday evening. Friday morning, the screen loaded in under a second. Nobody had to be told.

The Report With a Hole in it

A different client called us in a hurry. Their CFO had opened a department profitability report ahead of a board meeting and found a large “unassigned” line where there should not have been one. Almost a year of revenue in one customer segment, tagged with no department at all.

The company depended on department codes attaching to every transaction for their internal reporting to make sense. Every sale, every cost, every journal entry was supposed to carry a code so the leadership could slice the numbers the way the board wanted to see them. That had worked for years.

At some point, someone had added a new customer segment. Setting it up meant filling in many fields in the customer template, and the default department dimension was missed. Not deliberately. Just skipped. From that point on, every sale to a customer in that segment posted without a department code. No error. No warning. The transactions themselves were fine. They just weren’t tagged.

All the other reports continued to function as expected. Both total revenue and costs appeared accurate. The issue only became apparent when someone analyzed the data by department and discovered a category that wasn’t supposed to be there.

The fix on the setup side was small. We added the missing default dimension to the customer template so any new transactions would carry the tag going forward.

The cleanup was the longer job. We worked back through the affected transactions, determined which department each should have belonged to, and posted correcting entries so the historical reports would reconcile. The board meeting went ahead on the corrected numbers. The CFO didn’t have to explain why almost a year of revenue had been floating unattributed.

The dimension is the specific example, but the pattern is wider than that. Transactions can post without any of the pieces of data that reports and integrations quietly depend on. Posting groups, references, mandatory fields. Everything looks fine at the transaction level. The gap only shows when someone tries to read across the data and finds it doesn’t add up.

The pattern under both

The custom field wasn’t wrong. The dimension setup wasn’t wrong. Both were doing exactly what they were told to do. What they had in common is that nobody had looked at them again in years.

Custom code and standing setup drift the same way. Standard code gets tested, updated, and re-tested with every release. Custom code and custom setup sit where they were put. If they worked well enough at the time, they stay. And “well enough” gets more expensive every year, quietly, in ways nobody has to answer for.

If you can’t remember the last time someone looked at the custom pieces in your system, or at the setup that gets used every day without being touched, that’s the answer to how long they’ve been drifting.

What to do this week?

to do list

If any of this sounds like your system, don’t wait for a project. Do two things this week.

One. List the screens your team waits on. Not the whole system, just the ones people mention when you ask them which they dread. For each of those screens, ask whoever maintains your system whether the custom fields on them are recalculating from history every time they load, or whether they’re reading from something the platform already tracks. If the person can’t answer off the top of their head, half an hour with the right tools on a page load will tell them, and you.

Two. Run one report grouped by whichever dimension your business actually uses for internal slicing (department, project, region, whatever it is). Look at the “unassigned” bucket. If it’s bigger than you’d expect, or growing over time, something in your default setup isn’t catching everything that posts through it. That’s your signal.

You’ll have both answers by Friday. Whether the answers are comfortable is a different question.