This afternoon I merged seven GitHub repositories into one.

I’m not a developer. I want to say that early, because by the end of this post it might not sound true.

I wanted a website. One. Somewhere to put my blog and say what Andy Kemp Consulting does.

Today I have a main site, a blog, a free tools site, a documentation site, a demos site, a downloads site, a deployment platform with five products on it, a tenant audit service and an admin centre that looks after all of it, plus four personal apps that have nothing to do with consulting at all.

This is Part 5 of Building Again: My Journey into Vibe Coding, my personal series about what happens when an AI-assisted prototype starts becoming a real application. This time, it’s quite a few of them.

Earlier in this series I wrote about the first email, the first login and the first flat battery. This is the post about what it looks like a few months later, when it has all grown up a bit.

So, what have I actually built?

Here’s the estate, as it stands on a Sunday evening in November:

Every one of them leads back to the main site. That was a deliberate decision, and I call it “all roads lead to www”.

It took a while to get there. For a long time each site had its own header, its own footer, its own way of being deployed, and its own idea of what version each product was on. More on that later, because fixing it is what I spent today doing.

What I do, and what the AI does

People ask me how much of this I wrote. The honest answer is: almost none of the code, and almost all of the decisions.

I can read code. I learnt Pascal and Visual Basic a lifetime ago, and I spend my working life in PowerShell. But I don’t sit and write TypeScript, and I’ve never pretended to.

What I do bring is thirty-odd years of knowing what a system needs to do, who needs to use it, who must never be able to use it, and what happens when it goes wrong. That turns out to be most of the job.

So the split looks roughly like this.

What I do:

  • Decide what a tool or site is for, and what it must never do.
  • Set the security requirements: who signs in, how, with what permissions, and where the data lives.
  • Make the architecture calls. Cloudflare or Azure? One site or separate ones? Stored tokens or none?
  • Review what’s been built, test it as a user, and test it as someone trying to break it.
  • Say no. A lot.

What the AI does:

  • Writes the code, the tests and the build pipelines.
  • Explains frameworks I’ve never used, in plain English.
  • Finds the bug I’ve spent twenty minutes staring at.
  • Writes up the decision once I’ve made it.

That last one matters more than it sounds. Every significant decision gets written down as an architecture decision record: what the problem was, what we chose, what we rejected, and why. I’m up to eleven of them. When I come back to something three weeks later, or start a new session that knows nothing about the last one, the reasoning is still there.

The AI is very good at answering “how”. It still needs me to answer “should we?”

How it’s built

If you want to do something similar, here’s the stack. None of it is exotic, and most of it you can start using for free.

  • GitHub holds the code. Every change is a pull request, every pull request runs the checks, and nothing reaches the main branch without passing them.
  • Cloudflare runs everything. Workers and Pages serve the sites, D1 is the database, R2 stores the files and downloads, Cloudflare Access protects the admin centre, and Web Analytics tells me what people use without cookies or trackers.
  • Clerk handles sign-in for customers. One andykemp.com account gets you into the blog features, Tenant Audit and the deploy tools.
  • Microsoft Entra ID handles sign-in for me, with Conditional Access and PIM, because I’m not going to build a security business on an admin portal with a password on it.
  • SMTP2GO sends the email. Yes, that email. Part 2 of this series has a lot to answer for.

And this is how a change gets from my head to a live site:

Two things in that picture took me longest to appreciate.

First, the checks are the safety net, not me. I’m one person, often reviewing on my phone. If the only thing standing between a mistake and a live site is me noticing, a mistake will get through. So the checks test the code, but they also test the boring things: that every site shows the same version of each product, that the links in the footer still go somewhere, that the header matches. If any of that drifts, the build fails and tells me why.

Second, only what changed gets deployed. Change a page on the docs site and only the docs site goes out. The deploy platform, which can make changes in a customer’s tenant, doesn’t get redeployed because I fixed a typo in a blog post.

The security bit (the bit I actually know)

This is where I stop being a hobbyist and go back to being me.

It would have been easy to build all of this as one big website. One app, one database, one login, one deploy. AI would happily have written it that way, and it would have worked.

It would also have meant that a flaw in the blog’s comment form sat right next to the tool that holds sign-in tokens for a customer’s Microsoft 365 tenant.

So every site is its own security boundary.

A few of the rules I set, in plain terms:

  • The deploy tools store no customer tokens. When you use AVD Deploy or Access Baseline, you sign in to Microsoft in your own browser and the tool works with your permissions, there and then. My platform keeps a record of what was deployed: names, IDs and dates. Never a token, never a secret.
  • Each step asks only for what it needs. Read permissions to look, write permissions only for the step that writes. If you’ve signed in with more privilege than the step needs, the tool tells you, and tells you the smaller role you could have used. I spend my working life telling people about least privilege; my tools ought to practise it.
  • The admin centre is not on the internet in any useful sense. It sits behind Cloudflare Access, signs me in with Entra, needs phishing-resistant MFA, and my admin access is time-bound through PIM. When it talks to Tenant Audit, the deploy platform or my personal apps, it uses Cloudflare service bindings, Worker to Worker, not public URLs. And each app checks the admin’s token itself, so a bug in the admin centre can’t act on its own.
  • Secrets live in the platform’s secret store, and there are very few of them. The deploy platform’s only secret is a send-only email key.
  • The thing that publishes is not the thing that deploys. This one came up today. My blog editor publishes posts by committing to a GitHub repository. The new single repository holds the keys to deploy every site. If I’d let the blog editor push into it, a leaked blog key would have become a leaked everything key. So the posts stay in their own repository, and publishing a post asks the main one to rebuild. A small detail, and exactly the kind AI won’t raise unless you ask.

AI will build what you ask for. It’s your job to ask for the boundaries.

Today: seven repositories become one

Which brings me back to this afternoon.

Until today, the sites lived in seven separate repositories. Each one had grown up on its own, which meant each one had its own copy of the header, its own copy of the footer, and its own idea of the truth.

When I reviewed it all last week, the drift was embarrassing:

  • The header and footer had been copied by hand into six places, in five different formats.
  • Tenant Audit was listed as version 2.0.0 on the main site and the docs, while the product itself was on 3.2.3.
  • The AVD and W365 changelogs on the docs site were two releases behind.
  • A single product release could mean changing the same version number in five repositories.

None of that is a security incident. All of it is the sort of thing that makes a customer wonder what else hasn’t been kept up to date.

The rule now is simple. Anything every site repeats, such as the header links, the footer, each product’s name, address and version, is written down once. Every site reads it when it builds. If a product is released and I forget to update that one file, the tests fail and the release doesn’t go out.

It’s still separate sites. Each keeps its own subdomain, its own Cloudflare project, its own data and its own security headers. I considered putting everything into one big app and rejected it for exactly the reasons in the last section. One repository is a filing decision. One app would have been a security decision, and the wrong one.

Moving each site in was done carefully, one at a time, with the old repository left working until the new deployment was seen live. On the docs site, the history had to be rewritten on the way in, because each page shows when it was created and last changed, and a straightforward move would have reset every date to today. On Tenant Audit, the same 518 tests had to pass before and after. On the tools site, all 51 pages had to render the same.

I didn’t write any of those checks. I did insist on all of them.

If you want to do the same

If you’re like me, someone who understands systems and security but doesn’t write code for a living, here’s what I’d tell you.

  1. Start with one site. Not ten. A GitHub account and a Cloudflare account are enough, and both let you start for free.
  2. Write down what it must do before you ask for anything. Who uses it? How do they sign in? What data does it hold? Who must never see that data? Five sentences of requirements will save you five hours of rework.
  3. Never build your own sign-in. Use an identity service. Then remember that the service tells you who someone is, not what they’re allowed to see. That part is still yours.
  4. Write your decisions down. One short page per big decision. Your future self, and your next AI session, will need it.
  5. Make the checks do the remembering. Every rule you care about, from “the tests pass” to “the version number matches”, should be something the pipeline checks, not something you have to notice.
  6. Protect your main branch. Once your repository can deploy your sites, whoever can push to it can change your sites.
  7. Keep secrets out of code and out of prompts. Put them in the platform’s secret store, scope them to the least they need, and prefer setups that need no secret at all.
  8. Test it as a stranger. Sign in as a second user and try to see the first user’s data. Change the ID in the address bar. Call the API without the front end. If it works, you’ve found your next job.
  9. Get a second opinion. I often have one AI session review what another one built, with no knowledge of how it was built. It finds things.
  10. Know when to stop. Not everything you can build should become a product. Some of mine are deliberately early previews, and some will stay there.

So am I a developer now?

No. I still don’t think so.

I’m what I’ve always been: someone who knows how systems should work and what it takes to keep them secure. What’s changed is that I no longer need to hand that knowledge to a development team and wait. I can turn it into something that works, this evening, and test it before bed.

That’s the bit I find genuinely exciting. Not that AI writes code, but that thirty years of experience now has a much shorter path to becoming something real.

It’s also the bit that worries me when I see it done without the experience. The tools make it just as easy to build something insecure as something secure. They don’t know the difference unless you tell them.

AI can help me write the code. I still own what it becomes. And now, apparently, I own nine websites.

I promised in Part 3 that I’d write about where all these secrets go. I still owe you that one, and after today’s work it’s a much more interesting story.

Next in the series: Where Do All These Secrets Go? Credentials, signing keys, deploy tokens and why the best secret is the one you never needed.