Why I Govern AI Like I Governed Production Systems
A former CISO's approach to AI-assisted development: trust nothing, verify everything, and build the plumbing before you need it.
I spent the better part of two decades doing IT governance and cybersecurity work in South Africa, first at PwC, then as one of a small handful of IT internal auditors across the entire Bidvest Group, and eventually as a CISO at Bidvest Data. The through-line of all of that work was a fairly unglamorous principle: systems that aren't governed will eventually hurt you, and the ones that seem most competent are often the ones that need the most oversight. People nod along when you say that about production infrastructure or financial controls, but they look at you funny when you say it about AI coding assistants.
I'm building multiple software products mostly on my own now (medical billing, media audit compliance, news intelligence), and I'm doing it with AI-assisted development in an environment that I designed from scratch. Claude Code and Codex are genuinely good at writing software. They're also genuinely good at writing confident, plausible, subtly wrong software, and at quietly weakening your test suite to make a failure go away, and at spiralling through the same broken diagnosis four times in a row without noticing. Left to their own devices, they will ship broken code with the calm assurance of a contractor who promises your house extension will definitely be done by Friday.
So I govern them. Not because I think AI is specifically bad at development, but because I've spent my whole career watching what happens to systems that don't have controls around them, and the answer is always the same: they work fine until they don't, and then you're standing in the wreckage trying to figure out when things went sideways.
The setup, from my side, has a few moving pieces. There's a self-hosted Forgejo instance with CI enforcement (branch protection, required checks, the usual). Dev environments are containerised and derive from a single base image, with a CI pipeline that rebuilds every downstream project container whenever the base image changes, so the "works on my machine" problem stays solved across half a dozen projects. Credentials live in a self-hosted OpenBao instance, with each project getting its own short-lived, wrapped tokens instead of a flat .env file sitting in a repo somewhere. Logs and metrics from every container ship to a centralised stack so I can see what's happening without having to SSH into anything. And there's a shared knowledge volume mounted across all project containers, where conventions, service documentation, infrastructure docs, hooks, and skills are all accessible from any project at any time. That last piece is the one I'd most struggle to give up. Institutional memory that survives session boundaries is genuinely one of the hardest problems in AI-assisted development, since every new conversation starts with a blank slate unless you've built something to fill it in.
In terms of the actual governance, I've got north of a dozen automated controls in play now, and each one exists because I watched something go wrong enough times that I decided to encode the fix permanently. Some watch command exit codes and error patterns and force investigation when something fails silently, because without them Claude will cheerfully continue building on top of a broken foundation. Some protect the CI configuration and test suite from being quietly edited, which sounds paranoid until the third time you catch your AI assistant loosening an assertion to make a test pass rather than fixing the actual bug. Others refuse to call a task complete when tests are missing, because "I'll add tests later" is a lie whether a human or an AI says it. There are spiral detectors that watch the failure history within a session and flag when repeated errors suggest the diagnosis has gone sideways, and circular-reasoning detectors that catch the case where Claude proposes fix A, then fix B, then fix A again, apparently having forgotten it already tried that. And there's a whole layer dedicated to keeping context and memory coherent across long sessions, because the failure mode I see most often isn't bad code, it's an assistant that's forgotten what it learned ninety minutes ago.
The thing is, none of these hooks are blocking in the traditional sense. They inject reminders, surface problems, and enforce standards on Claude or Codex, but the developer (me, in this case) still makes the final call. This is basically the same principle as a good audit function: you're not there to stop the business from operating, you're there to make sure everyone knows what they're doing and why. The hooks guide and gatekeep, but in appropriate measures, which matters when you're a solo founder and the person being governed is also the person doing the governing.
The same controls run when I'm using Codex instead of Claude Code. That parity is deliberate, and it's been more work than I expected. The two tools have different hook protocols, different ways of declaring skills, different memory models, so a control that's a single shell script in one environment ends up being a Python translation in the other. But the principle is non-negotiable: if a control matters when Claude is doing the work, it matters when Codex is doing the work. Otherwise you've got two different governance regimes depending on which assistant you happened to invoke, and that's just inviting drift. So there's a parity layer that translates the same control objectives across both environments, and a shared memory layer so that lessons learned in one are available to the other.
The single most valuable artifact in the whole system is probably the conventions file, which is roughly 400 lines of hard-won behavioural rules that Claude Code reads at session start. Every line in that file represents a decision I only want to make once. Things like how to handle error boundaries, how to structure API responses, when to reach for a new abstraction versus keeping things flat, naming conventions that are consistent across projects. It's effectively a codified version of "here's how we do things around here," except "we" is me and an AI that has no memory of yesterday's conversation. Without it, every session starts from first principles, and you end up relitigating the same architectural decisions over and over, which is a genuinely maddening way to work (and I say that as someone with ADHD, so I have some expertise in things being maddening).
Also worth saying: I maintain a CI reliability scorecard with 67 items that I use to audit infrastructure across projects. This is basically a formalised version of war stories. Every time something breaks in a new and interesting way, it becomes an audit criterion. Over time, you build up a checklist that catches the kinds of failures you've actually experienced rather than the kinds you theorised about, which is always more useful. It's the same approach I used in internal audit, where the best audit programmes were always built from incident history rather than from frameworks downloaded off the internet.
The shared knowledge volume deserves its own mention because it solves a problem that most people working with AI assistants haven't even identified yet. When you're running multiple projects with AI assistance, each project generates lessons, patterns, and decisions that are relevant to the others. Without a shared knowledge layer, those lessons stay trapped in whatever project generated them. With it, a CI lesson learned in one project is immediately available in every other project. Cross-project institutional memory is something that large organisations spend millions trying to achieve, and it turns out that a mounted Docker volume with well-organised markdown files gets you surprisingly far. I'm taking this further using Obsidian to codify the knowledge, but the principle remains pretty much the same.
I know this whole approach looks like over-engineering if you squint at it from a distance. Initial velocity is definitely slower when you're building governance infrastructure instead of shipping features. But I've been doing this long enough to know that the time you "save" by skipping the plumbing is time you spend later, with interest, wading through the consequences. There's a metaphor I keep coming back to: nobody wants to build plumbing, it's unglamorous and invisible and you never get credit for pipes that work correctly. But if you don't build it right, you're walking in crap every day and pretending the smell is normal.
The deeper point, and the reason I think my background matters here, is that AI governance isn't a new discipline. It's the same discipline I've been practising for years, applied to a new kind of system. You wouldn't give an employee unaudited access to production and hope for the best. You wouldn't let a contractor modify your test suite without review. You wouldn't run financial systems without reconciliation controls. AI coding assistants are powerful (often more productive than a junior developer), but they have exactly the same failure modes as any other system that operates with autonomy and without oversight: they drift, they confuse confidence for correctness, and they optimise for the appearance of progress over actual progress.
I still use AI for basically everything I build. I'm not a sceptic, I'm a practitioner who happens to think that trust without verification is just negligence wearing a friendlier outfit. The governance isn't there because AI is untrustworthy in some fundamental way. It's there because nothing is trustworthy without governance, and anyone who's worked in audit knows that the systems that insist they don't need oversight are precisely the ones that need it most.