Building a SaaS Platform from Zero, Part 2: The Product Architecture That Powered the Business

The multi-tenant database solved our scaling problem. Data updates that had previously taken weeks now flowed to subscribing clients automatically. Onboarding went from a technical project to a configuration exercise. The foundation was solid.

But a database isn’t a product. Our clients weren’t paying for a well-designed schema; they were paying for access to healthcare provider data through reports they could run, exports they could pull, and analysis tools they could use during planning and open enrollment. The multi-tenant migration gave us a platform that could grow. Now we needed to build the thing that actually made money.

This is Part 2 of a three-part series about the architecture decisions behind a healthcare data SaaS platform — from proof of concept through acquisition. Part 1 covers the migration from a dozen duplicated client databases to a single multi-tenant system. Part 2 covers the product architecture and automation that made the platform scalable. Part 3 covers the cloud migration, system reliability, and what survived through acquisition.

Starting with what clients actually needed

The proof of concept had included a basic reporting interface. It had been enough to close those first dozen deals, but it was built to sell, not to grow with us. Adding features was painful, and as the client base grew, the limitations showed. Clients told us what wasn’t working, and honestly, we could see most of it for ourselves.

Before designing the new system, we went through the existing UI screen-by-screen and catalogued what was working and what wasn’t. Some workflows made sense and clients were comfortable with them. We kept those. Others were clunky or just got in the way, so we redesigned them. The goal was a new front-end that felt familiar where it should and better where it needed to be.

Every design decision started from a specific limitation or a piece of client feedback, and that discipline kept the scope honest and the timeline realistic.

The decision that shaped everything after it

As I designed the new application architecture, one decision ended up influencing almost everything that followed: I decoupled the data access layer from both the user interface, the reporting engine, and the database, and gave it an API. Data queries moved out of the application and into the database to improve performance.

In retrospect, and to most software architects today, this seems like an obvious choice. Decoupling your data access layer is accepted practice for any application beyond a trivial scale, and for good reason. But accepted practice in a textbook and a practical decision in a live system are different things. We had significant time-to-market pressure, paying clients who needed the product to keep working, and an existing codebase that had none of this separation. I had to carefully consider whether I could move away from the existing code structures in such a fundamental way without stalling delivery. The pattern was sound. The question was whether we could afford to implement it right now, and whether I could design the transition so the business didn’t feel it.

The reasoning for the decision was pretty straightforward once I looked at the uncertainties ahead of us. We knew the UI would go through design iterations, client needs would evolve, and we’d learn from watching how people actually used the product. If the front-end was tightly coupled to the data layer, every UI change risked breaking data access, and every data change would ripple into the interface. Separating them meant the UI team could redesign screens without worrying about what was happening underneath, and the data layer could evolve on its own schedule.

We had questions about whether our SQL Server backend would keep up as the client base grew. I wanted the option to swap out the database down the road without rebuilding the application sitting on top of it. The data access layer gave us that escape hatch. We never ended up needing it (SQL Server held up fine), though just before the acquisition, I was starting to explore ways to optimize a distributed database for better performance and reliability. The point is, having that separation meant those kinds of changes were on the table without touching the client-facing product.

And then there was the API itself. We built it because we had ambitions beyond a single application, things like client-facing API access, sibling applications, integrations. We dreamed big. In practice, we implemented a limited client-access API and built a separate internal application for our data team on the same layer. The grand vision didn’t fully play out, but the architecture meant that every new tool we built could tap into the same data layer without duplicating logic or creating parallel data paths. So even the modest version paid for itself.

The data access layer sat behind everything else, invisible to clients. It was the piece that gave us room to adapt as the product and the business changed around us.

It’s worth noting that this decision is even more relevant today than when I made it. If you’re thinking about adding AI or machine learning capabilities to your platform (and in 2026, you probably are), the first question is whether your data is accessible through a clean, consistent interface. LLMs and ML models need structured data access. If your data is buried inside a tightly coupled application, you’re looking at a major refactoring project before you can even start experimenting. Companies are scrambling to retrofit this kind of separation right now. We built it from the start because we were solving a different problem, but the architecture pattern is the same one you’d choose today.

The reporting engine that clients were actually buying

With the data access layer in place, we built the reporting engine on top of it. This was the product, and everything else existed to support it.

We gave clients two ways to get at their data. First, pre-designed reports through SQL Server Reporting Services, which was the standard analyses most clients needed, like provider comparisons and geographic network evaluations, the kinds of views that drove decisions during planning and open enrollment. Second, flexible data exports in XML and CSV formats where users could pick exactly which data points they wanted. That flexibility mattered because every client’s analytical needs were a little different, and building custom reports for each one would have recreated the same per-client maintenance headache we’d just gotten rid of with the multi-tenant migration.

The reporting engine became the company’s primary revenue driver, generating several million dollars a year. It’s worth thinking about what made that possible. The reports themselves were solid, but the real enabler was everything underneath, the multi-tenant database keeping data current and consistent, the subscription system controlling access, and the data access layer letting us build and iterate on the reporting interface without worrying about what was going on at the database level. The product worked because the architecture worked.

Building for the team, not just the client

Around the same time, the founder came to me with a request. The data team was struggling, and he asked if I could help.

I already knew the situation. When I’d been hired, and for about a year afterward, the data team had been cleaning hundreds of megabytes of data by hand, transforming it through saved SQL scripts, then running another set of queries for QA. Every update cycle was the same labor-intensive grind, and as we onboarded more clients and expanded data subscriptions, the volume just kept growing. The team had reached a point where they were about two weeks late on every cycle, and the gap was widening.

I built a mock-up of an automated import tool and showed it to the head of the data team. He wasn’t buying it. His team’s processes were complex, every data source had its own quirks, and he didn’t think a tool could handle the variety of transformations they dealt with day to day. Fair concern, honestly, he knew his domain better than I did.

We built it anyway, and the results changed his mind. The system automated the cleaning and transformation steps on import, applying business rules programmatically to the inbound data. On the QA side, we created screens that ran validation checks automatically and flagged issues for manual research, so instead of the team running queries and eyeballing results, they could focus on the exceptions that actually needed a human decision.

Manual data processing dropped by about 90%. The team went from falling further behind every cycle to keeping pace with a growing client base and expanding subscriptions. The outcome that mattered most to the data team leader was that he got his time back. Instead of managing a grinding, repetitive process, he could focus on data product development. That’s where his expertise was, and that’s where the business needed him.

One more thing worth mentioning: this application was built on the same data access layer as the client-facing product. Same data layer, same API, same logic. That was deliberate. It meant improvements to the data layer benefited both the clients and the internal team, and it kept us from building parallel systems that would inevitably drift apart over time.

If I were building that automation tool today, I’d absolutely evaluate large language models for parts of the data cleaning and transformation work. In fact, at my next role after the acquisition, I initiated a project that did exactly that, applying LLMs to automate client data file analysis and translation into standard database structures. But the underlying principle hasn’t changed. Automate the repetitive, high-volume work so your team can spend their time on the decisions that actually require human judgment. Ten years ago that meant a rules engine. Today it might mean an LLM. The architecture pattern (ingest, transform, validate, flag exceptions) is the same either way.

What the architecture was really about

The individual pieces (the data access layer, the reporting engine, the automation platform) each solved a specific problem. All of them shared a common design principle: build the layer that makes everything else possible, and build it so it can be replaced when the company needs something better.

The data access layer was a single decision that created options across the entire platform. It didn’t require predicting the future, just designing for the assumption that things would change. The UI would be redesigned. The database might need to be swapped. New applications would need the same data. Each of those turned out to be at least partially true.

The internal tooling mattered just as much as the client-facing product. The data processing automation is the kind of work that’s invisible to clients, but it determined whether we could keep our promises to them. Without it, the data team would have become the bottleneck as we grew from a dozen clients toward 75. The platform’s scalability was more than an architecture story. It was also an operations story, and the internal tools were what made the operations work.

In my experience, the companies that scale well invest in the infrastructure their internal teams need to keep up with growth. It’s tempting to pour every resource into the client-facing product, because that’s where the revenue shows up. The internal systems (data processing, quality assurance, operations) are what determine whether your product can actually deliver at scale. Build for your team, and your team will build for your clients.

And if you’re thinking that AI changes this calculus, well, it doesn’t. It raises the stakes. A well-structured data layer, clean access patterns, and solid operational tooling aren’t pre-AI relics. They’re the foundation that AI capabilities need to sit on. The companies getting real value from AI right now aren’t the ones with the fanciest models. They’re the ones whose data architecture was ready for it.

Next in this series: Part 3 covers the cloud migration, the infrastructure crisis that forced it, and what happened to the platform through acquisition — including which architecture decisions survived and which ones didn’t.

Leave a comment