Foundational CI/CD: No Magic, Built to Scale

Case study - CI/CD and auth for an LLM-native team

Stratodata, a pre-Series A analytics SaaS that builds a large share of its own code with LLMs, needed production-grade infrastructure before it could trust itself with real users. I built it in phases: a working CI/CD pipeline in the first two weeks, an auth layer wired in by week two, hardening and handoff by week four. Every phase works on its own - which matters, because nobody's roadmap moves on someone else's schedule.

Here's how that went.

The state I walked into

Deployments were done from developer laptops. "Latest" was pinned everywhere - not a version, the word latest, meaning a different thing every time you typed it. A real GitHub token sat hardcoded in dbt_service.py. The encryption key was changeme-key-32byteslong1234567890! - someone meant to change it. Zero test coverage across 146 files. No rollback: a bad deploy meant redeploying manually and hoping the fix was fast. And using LLMs meant broken deploys weren't rare.

None of this was unusual, and none of it was a judgment on the team. It's what startup pace looks like. You don't get to stop and ask how this actually gets to prod - not until you get customers. And that was starting to happen.

Why infrastructure first

The instinct at most startups runs the other way: ship the feature, fix the plumbing later. But auth is sensitive - you can't confidently ship it without a build you trust. Observability needs artifacts - you can't connect a log line to a specific build if every build is "latest". Every refactor after this one is a bet, and CI/CD is what lets you take the bet back if it's wrong. The CI part also forces LLMs to behave, as it does humans.

I've built this foundation more than once, at more than one company. The order's similar every time: the support structure isn't glamorous, but it's what makes everything built on top of it trustworthy.

Four weeks, not a waterfall

Week one: Docker builds, an artifact registry, deployments running through GitHub Actions instead of whoever's laptop was closest - no separate CI server to stand up or maintain. Database migrations and auto-release running end to end.

Week two: Zitadel wired into the actual application, not just deployed next to it - Google and GitHub login working, an invite flow, an organization model replacing manual database edits. A working dev environment two weeks in, not six months in.

That's also when things broke, the way integration work always breaks. A company-ID bug surfaced mid-build - traced and patched the same day. A user could start a Google login and create a second account on an email that already existed - fixed by making that fail on purpose instead of half-succeeding quietly. Nothing dramatic. The kind of friction you only find by actually wiring pieces together, not by reviewing them separately on paper.

By week four: quality gates on every commit - type checks, linting, security scanning. Database migrations validated in CI before they ever touch production. Structured logging with a runbook for turning log values into alerts. Milestones one and two, delivered.

After week four: the original scope ran six weeks - the rest stayed on the table for whenever they're ready to pick it back up.

One more thing worth naming: a good chunk of Stratodata's actual code gets written by an LLM, not typed by hand - for this team that's not an edge case, it's the default. Quality gates matter more in that world, not less. Type checks, linting, and security scanning catch exactly the class of mistake code-gen produces fastest - a confidently wrong import, a config that looks fine and isn't, a typo nobody would type by hand but a model will happily generate. Rollbacks dropped hard once those gates went in. Not because anyone got more careful. Because the pipeline got faster at catching what a person, or their LLM, missed.

Before and after

Before: deploy meant run the script, cross your fingers, and find out the hard way whether it went wrong. After: merge to main, watch the pipeline, confirm the version in production.

The concrete difference - a bad deploy used to cost most of a day: notice it, trace it, redeploy by hand, hope the fix held. After, it's a rollback to latest-1. Minutes. Nobody's scrambling to remember the old process before a client call, because there's no longer an old process to remember.

Worth saying plainly: it's not perfect. Two edge cases still show up in production - permission overlap when a single user sits inside more than one organization, and email delivery failures for customers running custom domains. Neither one gets discovered by an angry user. Stratodata catches both by running the end-to-end test suite manually before a customer would ever hit them, traces the fix with an LLM doing the legwork, and manages the rest through the admin tooling that shipped as part of the same package.

That's a real gap, and I won't dress it up as nothing. But there's a wide difference between a known, bounded edge case a team manages on their own schedule, and a surprise a customer reports at the worst possible time. Building toward the first instead of the second was the actual point of all of this.

Built to stand alone

After this phase, Stratodata paused - time spent on customer fit and testing before committing to the next round of work. They've had selling to do, and a good enough pitch. A completely ordinary startup instinct. It also means the phase structure did its job: there was a demo-ready app to pause with.

The pipeline kept running. The auth kept working. Nothing about the pause broke anything, because nothing was designed to require me still being in the room. We'd chat on occasion, they've got my number, and other things needed doing.

A foundation that only holds together while its builder stays on retainer isn't a foundation. It's a leash. Whatever comes next - more phases, a different partner, or another stretch of getting the product in front of customers before touching the roadmap again - the CI/CD layer and the auth layer keep running exactly as delivered, on their own terms.

Juraj put it plainly afterward:

"Before Tin came on board, our deployments were executed from my laptop and our auth layer was just a copy-pasted Google widget. He built a proper CI/CD pipeline and integrated Zitadel from scratch - two weeks, and it's been running well since. On top of that, the way he restructured the repo made a real difference - working with it using LLMs is noticeably easier now and they produce better quality code. The communication was clean, the delivery was clean, and when we paused mid-project he gave us an honest read on what was still missing. That last part is rare. We could own the budget and use it open-eyed."

— Juraj Kapasny, CTO at Stratodata

This is the first thing I assess in any new engagement: not what feature ships next, but whether what's already shipped can survive you stepping away from it.

If your infrastructure needs the same kind of foundation - let's talk →

Book a discovery call