What staying on after launch costs us
The last feature we shipped was a fee waiver approval flow for a school ERP. The principal signs off, finance adjusts the ledger, parents see the revised total. We launched it on a Thursday. On Friday morning, a parent called the office because the waiver showed up on the invoice but not on the receipt PDF.
We fixed it that afternoon. The fix was a one-line template change — the receipt generator was reading from a stale copy of the fee record instead of re-fetching after approval. But the point is that we were the ones who caught it, understood it, and patched it within hours. We still had the context.
Most shops our size don't work this way. The standard model is to build, hand over, and move on. Maintenance gets handed to a separate team or a support contract with a different vendor. That model has real economic logic behind it — new projects pay better than maintenance, and context decay makes old code expensive to support. We know this. We stay on anyway.
What it costs us is straightforward: we take on fewer projects per year than a three-person team at our skill level could otherwise handle. A new client engagement means committing to supporting that system for the next two or three years, sometimes longer. That slot is not reusable until the system stabilizes or the client's internal team takes over. We've turned down work twice this year because taking it would have stretched our support capacity past what we consider safe.
What it changes about how we build is less obvious but more interesting.
When you know you'll be the one answering the phone on Friday morning, you make different decisions on Wednesday afternoon. We write more migration scripts than most teams our size, because recovering from a bad data change at 11 PM is something we'll actually have to do. We prefer boring dependency choices — Postgres over a newer database, a standard auth library over a custom implementation — because the cost of an exotic dependency breaking at the wrong time falls on us.
We also build observability into systems that probably don't need it yet. A school attendance tracker with 200 users doesn't strictly need structured logging and alerting. But when a teacher reports that attendance didn't sync, we want to open the logs and see exactly what happened rather than guessing. That's an hour of setup we wouldn't do if we were handing the system off.
The tradeoff is that our initial delivery timelines run longer than a throwaway-build shop. A client who just wants an MVP out the door in six weeks can find someone faster. We usually take eight to ten because we're thinking about the migration path, the rollback story, and the parts of the system that will need changes in year two. We make this explicit in scoping conversations. Some clients don't want that and go elsewhere, which is fine.
The benefit on our side is that we end up with systems we actually understand deeply. The fee waiver bug took two hours to diagnose and fix because we wrote the approval flow, we designed the ledger adjustment logic, and we knew the receipt generator was a separate codepath. No archaeology, no reading someone else's commit history to figure out why a thing works the way it does. The context is still warm.
There's also a compounding effect. Each year we maintain a system, the client's trust in us grows, and the system gets more stable because we've fixed the edge cases that surfaced in real use. The school ERP we started in 2022 now generates maybe one support ticket a month. That frees capacity for the next project without dropping the old one.
We don't stay on because maintenance is glamorous. We stay on because the software we build is running real operations, and the people using it call us when something breaks. Being the team that picks up the phone is the commitment, and it shapes how we write code from the first commit.