Most agencies sell hours. You scope the work, you ship it, you hand over the keys, and the moment the invoice clears the consequences of every decision you made become someone else's problem. We have run that model for years and it works. But it has a blind spot, and the only way to close it is to build something you cannot walk away from.
So we built Markup, a quoting, invoicing, and compliance platform for Canadian trades contractors. We design it, we ship it, and we operate it in production, on call for the support tickets and the billing edge cases and the 2 a.m. outages. This post is about why an agency would take that on, and what it changed about the work we do for clients.
The blind spot in client work
When you only build for clients, your relationship with a codebase ends at launch. You can ship something that demos beautifully, passes the acceptance tests, and quietly becomes a maintenance nightmare eighteen months later, and you will rarely feel it. The pain lands on whoever inherits the code, not on the team that wrote it.
That gap shapes habits in ways that are hard to see from the inside. You optimize for the launch, not the third year. You make architecture calls you never have to live with. You estimate the cost of a feature without ever paying the cost of running it.
Operating your own product removes the exit. Every shortcut we take in Markup, we trip over ourselves. Every dependency we add, we patch ourselves at midnight when it ships a breaking change. That accountability is the entire point. It is the difference between knowing in theory that a decision is expensive to maintain and feeling it land on your own roadmap.
Why a product, not a side project
Plenty of agencies have a graveyard of half-built internal tools. We did not want another one. For this to be worth the distraction it had to be a real product with real users who pay, churn, and complain. Anything less and we would just be writing demos again.
That meant picking a problem narrow enough to actually solve and underserved enough to matter. We landed on Canadian trades contractors: electricians, plumbers, HVAC technicians, roofers, general contractors. The back office of a trades business is buried in rules that the big US-built tools mostly ignore. Provincial Construction Acts dictate when invoices start the payment clock. The CRA wants T5018 subcontractor reporting. Liens have preservation deadlines that vary by province and do not forgive a missed date. Holdback math is its own small discipline.
None of that is glamorous. All of it is real, recurring, and badly served by software that treats Canada as a checkbox on a US-first product. A narrow, unglamorous, rule-heavy problem is exactly the kind of thing a small team can build a defensible product around. So that is what we did.
The technical decisions worth sharing
The first decision was the least exciting and the most important: we built Markup on the same stack we put in front of clients. TypeScript end to end, Next.js on the App Router, PostgreSQL, deployed on infrastructure we already know. If we are going to recommend a stack to people paying us to be right, we should be willing to bet our own product on it. We write about why we default to TypeScript on large codebases and how we think about server and client components; Markup is where those opinions get tested under real load instead of in a blog post.
The hardest part was not the invoicing or the UI. It was the compliance logic, because compliance is where "mostly correct" is the same as wrong. A province-aware tax engine has to get GST, HST, PST, and QST right across all thirteen jurisdictions, every time, with no rounding surprises that a bookkeeper will catch three months later. We model each rule as data, not as branching code scattered through the app, so that when a province changes a rate or a threshold we change one entry and not twenty if statements. The same discipline drives lien deadline tracking and holdback calculations: the rules live in one place, they are versioned, and they are tested against the actual statutes rather than our memory of them.
That constraint, correctness as a hard requirement rather than a nice-to-have, raised the bar on everything around it. You cannot ship a tax engine on vibes. You write the tests first, you handle the edge cases before launch, and you build the audit trail in from day one because someone will eventually need to prove why a number is what it is. It is a healthy discipline to carry back into client work.
What operating a product taught us that client work never did
This is the part you cannot learn from shipping and walking away.
- Support is a feature, not an afterthought. Every confusing label, every unclear error message, every edge case you did not handle becomes a ticket. Tickets are expensive and they are honest. They tell you exactly where the product is unclear, and there is no project manager in between softening the feedback.
- Uptime is a promise you feel personally. When a client's site goes down, it is a bad day. When your own product goes down, paying customers cannot invoice, which means they cannot get paid, which means your problem is now their cash flow. That reframes how seriously you take monitoring, backups, and graceful degradation.
- Billing has more edge cases than the product. Trials, proration, failed cards, refunds, plan changes mid-cycle. The money path is where polish actually shows, and it is the part most demos skip entirely.
- The real cost of a feature is the cost of running it forever. A feature is not done when it ships. It is done when it has survived a year of real usage, a few dependency upgrades, and at least one edge case nobody predicted. We estimate differently now because we have paid that bill on our own product.
None of this is visible from a project that ends at handoff. All of it makes us better at the projects that do.
How this changes what clients get
The honest answer is that running Markup made us more conservative in the right places and more confident in others.
More conservative, because we have now felt the long-term cost of clever decisions and we are quicker to talk a client out of complexity they will have to maintain. More confident, because when we recommend a stack, a hosting setup, or an architecture, it is not a recommendation from a slide deck. It is the thing we chose to bet our own product on, and we can tell you exactly where it has hurt and where it has paid off.
It also means that when a client wants to build a product rather than a website, we have actually done it. We know what the first version should and should not include, how to scope the compliance and billing work that always gets underestimated, and what it really takes to operate the thing after launch. That is a different conversation than "we can probably figure it out."
Should your agency build a product?
Maybe not. It is a real distraction, it competes with billable work for attention, and most internal products quietly die because no one is accountable for them. If you are going to do it, treat it as a product with real users from day one, pick a problem narrow enough to actually win, and commit to operating it rather than just shipping it. The operating is where the learning is.
For us it was worth it. The accountability of running our own software closed the blind spot that client-only work leaves open, and everything we learned doing it flows straight back into the work we do for everyone else.
If you are weighing whether to build a product or want a team that has actually shipped and operated one, talk to us or read more about how we approach web and product development.
Tags