Some Bugs Only Show Up Live

This week I did something I don't do enough. I stopped building a feature on my own, shared my screen, and renewed real contracts with a client while their team watched.

It turned out to be the most useful hour of the week. Here's the whole story, start to finish, and what I think you can take from it whether you're building custom software, buying it, or just rolling out a new tool to your team.

The problem: renewals that lived in someone's head

One of our clients is a services business that runs on recurring contracts. Every contract renews on a monthly cycle, and every renewal touches a lot of moving parts:

  • The contract itself and where it is in its cycle
  • The permit behind the job, which has its own clock and its own fees
  • What's been invoiced and what's been paid
  • Whether the equipment has been removed, or is scheduled to be
  • The new invoice, which has to be created in their accounting software

Before we started working together, their team handled all of that from a mental checklist. One person would look at active and past-due contracts in one place, check removals somewhere else, open the accounting software to look at invoice history, and then check messages to see if the customer had asked for any changes. Only then would they create the next invoice.

It worked because the people doing it were good at it. But it didn't scale, and it was easy for a contract to slip a few days, then a few weeks, past its renewal date. When that happens, you're not just late on one invoice. You're now reconciling history to figure out which cycle the contract is actually on.

They also had a good reason not to just turn on automatic invoicing in their accounting software. They'd tried it. The problem was that automatic invoices went out before anyone could catch a change the customer had asked for. So they turned it off and went back to doing it by hand.

That's the part I think a lot of people miss about automation. The goal usually isn't to remove the human. It's to give the human one place to look, so the decision is fast and the boring parts happen on their own.

What we built: one screen for the whole decision

We built them a renewal view that pulls everything into one place. You can see which contracts are expiring soon, which ones expired recently, and how many days past expiration each one is. You click into a contract, check the details, and renew it. The app creates the invoice in their accounting software and links it back to the contract.

Over the last couple of weeks we've added a lot around that core:

  • Printable statements at both the contract level and the property level, which their team was excited about because their accounting software doesn't have a separate statement feature
  • Voiding an invoice in either system now syncs to the other
  • Comments on invoices, so notes live with the record instead of in someone's inbox
  • Clear indicators showing which customers have active contracts and open invoices
  • A single property per contract, so the same job site doesn't get entered twice
  • An easier way to link customers in the app to customers in the accounting software

All of that was tested. By me. With my test data. And it worked every time.

What I tried first, and why it wasn't enough

Here's the honest part. For weeks, I tested renewals the way most developers do. I'd create a test contract, click through the renewal, and confirm the invoice landed in the accounting software with the right line items. Happy path, every time.

But their team kept running into renewals that would stall. Not every one. Just often enough to be frustrating and hard to pin down.

So on Wednesday's call, I stopped walking through my update list and asked if we could try a renewal together, right then. I said something I've been thinking about since: some of this I really can't test on my own, because it's specific to how your team works. There are a lot of nuances, and it's easier for me to see them in real time than to guess at them.

The working session

We pulled up a contract that was ready to renew. I said I wouldn't click anything until they told me it looked right. They checked it, I clicked renew, and the app stopped and asked me to verify the customer link for that company.

That had never happened in my testing.

It turned out their accounting software had a few older, inactive customer records with similar names to active ones. That's completely normal for a business that's been around for years. My test data had none of it, so I'd never hit the case where the app had to figure out which record was the right one.

I said, "Give me five minutes," turned my mic off, and fixed it. Then we renewed the next one together.

We kept going and found a few more small things:

  • On renewals, the invoice date was defaulting to today instead of the date the cycle actually starts
  • A status label said an invoice had been sent to the accounting software before it actually had been
  • A couple of fields, like the sales rep, weren't editable during renewal when they needed to be
  • Service dates needed a consistent home on the invoice, so we moved them to the customer memo instead of tucking them into line items, where they'd disappear if someone used a different item

None of those are dramatic. But each one is the kind of thing that makes a person stop trusting a tool and go back to their old way of doing things. And none of them would have shown up in my testing, because my testing didn't look like their actual week.

What changed

It went well enough that before Friday's call I sent a short note asking to make part of it a renewals working session too: contracts expiring in the next week and anything that had expired in the last 30 days. We renewed contracts live on the call, and the conversation had moved from "why did this break" to "what should this do next." That's the shift you want.

A few of the next steps came straight out of watching their process:

  • Transfers that keep their history. When a contract changes hands, it starts over at cycle one, but the permit and its original application date carry over. The city doesn't let you restart a permit, so the history has to follow the job.
  • Months active on every permit. Permit fees go up the longer a permit is open, so we're adding a "months active" count to the permit, the contract, and the renewal screen. That way the person renewing can see when pricing needs to change without doing the math.
  • A manual cycle number for older contracts. Some legacy contracts have gaps in their history. Instead of forcing the team to reconcile years of records before they can bill again, they can set the current cycle and keep billing while they reconcile in the background.
  • A better quote form. We noticed quote requests from their website often came in with almost no detail about the work, which meant a follow-up call every time. We're making the scope of work field required to get better information up front.
  • Expired contracts that stay visible. Contracts marked for removal now stay on the current list until their expiration date passes, so nothing quietly falls off.

We also set up the simplest feedback loop I know: when something doesn't work, they tag me in a comment on that record. I see exactly which contract it was and what they were trying to do. No screenshots, no long email threads, no "it broke sometime yesterday."

What you can take from this

1. Real data is messier than test data, and that's the point

Test data is clean because you made it. Real data has years of history, duplicate records, inactive customers, workarounds, and that one account that was set up differently years ago. That mess isn't a problem with the business. It's the business. Any tool you build or buy has to handle it.

Try this: Before you call a tool done, run it against your oldest, messiest real records, not your newest clean ones.

2. Watch the person who does the work every day

The fastest way to find what's wrong with a tool is to sit with the person who uses it most and watch them use it. Not a demo. Not a training. Just them doing their actual job while you take notes.

Try this: Book 30 minutes. Ask them to do a real task in the tool while you watch. Don't help, don't explain, just write down every time they hesitate. You'll learn more in that half hour than in a week of testing on your own.

3. Test in the right order

On another project this week, I sent a client the order I'd recommend testing in, and it applies here too:

  1. Data structure. Does everything save and show correctly, and can you see and edit every piece of information you need?
  2. Functionality. Can you get the job done? It might not be the fastest way yet, but if you can get it into the system, we can make it better, faster and easier later.
  3. User experience. Is it as intuitive as you'd expect?
  4. User interface. Does it look clean?
  5. Speed. Loading and saving delays come last, once you know where everything lands.

People tend to start at the bottom of that list because it's what they notice first. But a fast, pretty screen that saves the wrong date is worse than a slow one that gets it right.

The bigger idea

The best software we build doesn't come from me guessing what a team needs and then disappearing for a month. It comes from building something real, putting it in front of the people who use it, and fixing what we find together. Sometimes that means turning my mic off for five minutes in the middle of a client call. I'm good with that.

If there's a tool your team technically uses but quietly works around, I'd love to hear about it. Reply to this email, or if you'd rather talk it through, grab 30 minutes with me here.

Talk soon, Patrick