How a small experiment with Claude Code grew into an identity operations platform and changed the way I think about AI-assisted software development.
I didn’t set out to build an identity platform.
During my New Year break, I started experimenting more seriously with Claude Code. At the same time, I had a recurring problem in my day-to-day work that had been bothering me for years.
Whenever I wanted to review access across our environment, I had to jump between multiple systems, export data, compare spreadsheets, check HR information, cross-reference the corporate directory, and manually work out who was who.
Most applications knew very little about the person behind an account. They might have an email address and a full name, but not necessarily the person’s department, job title, employment type, manager, or even whether that person still worked for the company.
So a seemingly simple question such as “Does this person still need access to this system?” could require several data sources and a surprising amount of manual work.
My original goal was simple: bring the information together so I could spend less time collecting data and more time deciding what actually needed attention.
Eight months later, that small experiment had grown into a platform that pulls together identity and access information from dozens of systems, models thousands of entities and tens of thousands of relationships, and continuously checks the environment for inconsistencies.

The surprising part was not how quickly AI helped me write the code.
It was how much work it still took to make the result something I could actually trust.
The first version was much smaller
My first idea was almost embarrassingly simple: pull users from our main identity sources and a few applications, match them mostly by email address, and show where each person had an account.
At the time, I was still discovering what Claude Code could realistically do, and I was thinking about the problem mainly as user management. I wasn’t thinking about identity graphs, reconciliation engines, expected state, or governance workflows. I just wanted to stop opening the same systems and spreadsheets over and over again.
The first version saved time immediately, but as I connected more systems, the model began to break down.
Users were only one part of the picture.
Groups mattered because they granted access. Then there were shared mailboxes, service accounts, shared accounts, applications, and other resources that were not “users” but still mattered when evaluating access. Contractors, partners, external providers, and other identities also didn’t always exist in the same authoritative systems as employees.
The question was no longer simply:
Where does this user exist?
It became:
What is this identity connected to, what does it own, what does it have access to, and why?
That was when I restructured Identity Hub around entities and relationships rather than users.
A person could be an entity. So could a group, application, organization, shared resource, or service account. Relationships could represent membership, ownership, management, access, delegation, or affiliation.
What started as an easier way to search across systems gradually became a model of how identities actually moved through the organization.
Reality is messier than the data model
One of the most important lessons came from something that sounds obvious in hindsight: rarely does one system know the whole truth.
HR may be authoritative for employees, but some contractors may not exist there at all. An application may know whether an account is enabled while knowing almost nothing about the human behind it. Another source may understand the person’s organizational context but know nothing about their application access.
And then there are exceptions.
Early in the project, I created a Knowledge Base inside Identity Hub to capture organizational context that otherwise existed only in my head: which sources mattered, which populations should be treated differently, what exceptions existed, and why some apparently unusual situations were actually valid.
I also used that context when working with Claude. Before adding an integration, I would have it review the relevant business logic and API documentation, identify the data we needed, and go through the design with me before implementation.
That worked well, but it eventually exposed an important limitation.
Documentation can explain the rules. Code has to enforce them.
A deterministic system cannot depend on whether an AI model happens to interpret a paragraph correctly in a particular session. Important business rules like source authority, precedence, matching logic, lifecycle state, and exception handling had to become explicit software behavior.
That realization changed the way I approached the project.
A successful sync can still be wrong
The early versions produced results quickly, which was exactly what I wanted from a side project. They also produced technical debt quickly.
As the system expanded, I began finding situations where the data looked correct at first glance but wasn’t. Some edge cases had not been considered. Some integrations made perfectly reasonable assumptions that did not apply in our environment. Sometimes correcting one issue exposed another.
Eventually, I realized fixing individual bugs wasn’t enough. I needed stronger guarantees about the system’s behavior.
An API request returning HTTP 200 was not enough.
A sync job completing successfully was not enough.
Even a sync with zero reported errors could still leave me with an inaccurate representation of reality.
Imagine an account that is still active inside an application after the person behind it has left the company. The application itself may be perfectly correct: as far as it knows, the account exists and is enabled. HR may also be correct in showing that the employment relationship has ended. The corporate directory may correctly show the primary identity as disabled.
None of those systems is necessarily wrong.
The problem exists between them.
It only becomes visible once enough context is correlated to ask a different question: not “Did the API return the account?” but “Does the state of this account still make sense?”
That distinction became central to Identity Hub.
The goal became determinism. If nothing had changed in a source system, running the same synchronization twice should not generate artificial changes. The order in which APIs returned data should not affect the resulting state. Partial datasets should not accidentally cause valid information to disappear, and a non-authoritative system should not overwrite an authoritative attribute simply because it returned a value.
Most importantly, when the system encountered something it could not safely understand, it should fail conservatively rather than confidently invent an answer.
The biggest risk was no longer that the software would crash.
It was that it would keep running while giving me false confidence.
Trust had to be earned
I did not start trusting Identity Hub because the test suite passed.
Trust accumulated slowly.
Because I knew the underlying systems well, I could repeatedly compare the platform’s conclusions with the actual sources, especially for unusual identities, lifecycle states, ownership relationships, and access patterns.
Testing also became much more deliberate. Instead of asking AI to implement large areas of functionality in one go, I moved toward smaller phases: research, design, implementation, testing, review, then the next step.
I also separated review concerns. Rather than expecting one large AI context to implement a feature and then judge its own work, I began doing focused reviews for bugs, security, documentation consistency, business-rule alignment, and edge cases.
Over time, what had started as experimentation became a repeatable engineering process.
That led me to one of the broader lessons I took from the project:
The faster code can be produced, the more disciplined we need to become about what we produce.
AI dramatically reduces the cost of creating an implementation. It does not remove the cost of understanding whether that implementation is correct.
In some ways, it makes that distinction more important.
From manual reviews to continuous assurance
The original goal of Identity Hub was to save time, and it did that almost immediately. Once I began trusting the data, however, the value changed.
Previously, a broad access review could require hours of preparation, sometimes much longer depending on the scope. Before investigating anything, I first had to gather information from multiple sources, determine whether it was consistent, identify who the accounts belonged to, and reconstruct enough context to understand what I was looking at.
Now most of that correlation happens continuously.
Instead of compiling the information, I can look directly at findings: an offboarded person who still has active access, an account that cannot reliably be mapped back to a person, access left behind after a role change, conflicting attributes between authoritative systems, orphaned shared or service accounts, unexpected memberships, or discrepancies between HR, directory, and application state.
These are not theoretical categories. They are the kinds of issues that began surfacing once enough information could be correlated reliably.
The biggest change, then, was not simply speed. It was the operating model.
Checks that were previously periodic because they were expensive and repetitive could now happen continuously because the underlying systems synchronized throughout the day.
I moved from collecting and comparing information to deciding what actually required intervention.
That is much closer to continuous assurance than a traditional manual access review.
Actual state is not enough
Once the system could describe actual access reliably, another question became unavoidable:
What should someone have?
That led me to model expected access patterns based on factors such as role, department, location, and employment type, not as rigid templates, but as baselines against which meaningful deviations could be detected.
That distinction matters for more than reporting.
Without an expected state, automation is simply execution.
If a system is eventually going to provision or remove access safely, it first needs a reliable definition of what “correct” is supposed to look like.
And once automation begins to affect production access, the consequences of being wrong change significantly.
A read-only system can produce an incorrect finding. A write-enabled identity system can make an incorrect change.
For that reason, my goal is not maximum automation. It is maximum safe automation.
The principle I keep coming back to is simple:
Known, approved, and deterministic: automate it.
Ambiguous, exceptional, or privileged: keep a human in the loop.
In some cases, the right intermediate step may be one-click remediation rather than full automation. Approvals can continue to happen through existing workflow systems while Identity Hub acts as the governance and execution layer behind them.
And automation should not simply act. It should verify afterward that the resulting state matches what was intended.
What Claude Code actually changed
Because Claude Code was involved from the beginning, there is an obvious question:
Did I build Identity Hub, or did the AI build it?
Claude removed an enormous amount of mechanical work between an idea and working software. It wrote code, researched approaches, implemented designs, created tests, and helped review results.
Without that leverage, I almost certainly would not have attempted a project of this scale on my own.
But Claude did not decide what Identity Hub should become.
It did not know how the organization worked until I explained it. It did not know which system should be authoritative in a particular situation. It could not decide which inconsistencies mattered operationally, what level of risk was acceptable, or whether a technically elegant implementation actually reflected reality.
Those decisions remained mine.
At different stages, I found myself thinking like a product owner, architect, project manager, security reviewer, QA engineer, development lead, and operator.
The most useful skill I developed was not “prompt engineering.”
It was learning how to collaborate with AI critically.
AI can make assumptions with enormous confidence. It can build an elegant implementation around an incorrect interpretation. It can miss an edge case that someone with years of operational experience recognizes immediately.
And because the output arrives so quickly, it is surprisingly easy to mistake visible progress for understanding.
The more capable the AI became, the less willing I became to accept its output at face value.
In that sense, working with AI became surprisingly similar to working with people. You learn what context needs to be explicit, what can safely be delegated, where review matters most, and when you need to challenge an answer that looks convincing.
What changed in the way I build
The project changed not only what I built, but how I approach building software with AI.
If I started again today, I would spend more time on design before implementation.
AI makes visible progress incredibly easy. You describe something and minutes later there is a working screen, API endpoint, migration, or integration. That feedback loop is powerful, but it can also encourage implementation to move faster than understanding.
Some of the most expensive changes I made later could have been avoided by thinking more deeply about the model earlier.
I would also work incrementally from the beginning. Large AI-assisted implementations create large contexts, large review surfaces, and more opportunities for hidden assumptions to survive unnoticed. Smaller phases are easier to understand, test, and challenge.
Most importantly, I would ask more questions before asking for code.
Why should this source win?
What exactly does “inactive” mean here?
What happens if an API returns only part of the expected dataset?
What assumptions are we making about identity matching?
If a sync succeeds, how do we know the resulting state is correct?
Those questions turned out to be far more valuable than simply asking whether the code worked.
AI changes build vs. buy. It does not eliminate it.
There is one conclusion I would not want anyone to take from this project:
Just because AI makes custom software easier to build does not mean we should build everything ourselves.
Mature commercial identity governance products exist for good reasons. Custom software creates responsibilities around security, architecture, testing, maintenance, documentation, resilience, and long-term ownership.
Being able to build something is not, by itself, a reason to build it.
What AI changes is the economics of the decision.
A domain expert can now realistically explore and build focused internal software that might previously have required a dedicated development team. That becomes particularly interesting when the problem is highly specific to how an organization operates.
Identity Hub creates value because it can model details, relationships, and exceptions specific to the surrounding environment.
But that flexibility is not free. Every custom rule becomes something that must be understood, tested, maintained, and eventually handed over.
I wouldn’t suggest that every company build its own identity platform.
We should revisit some assumptions about where custom internal engineering can create disproportionate value.
The build-versus-buy question still exists.
AI moves the line.
The part that matters most
Identity Hub started because I was tired of doing a repetitive task manually.
It grew because every improvement created another possibility. Better correlation enabled better checks. More reliable data enabled safer automation. Turning knowledge that existed in people’s heads into explicit rules made the entire system more useful.
It also became one of the most valuable learning projects I have worked on. I had to understand software architecture, data modeling, testing, security, product design, and engineering discipline more deeply than I had before.
When I started, the most impressive thing about AI-assisted development was how quickly it could produce code.
That is no longer the part I find most interesting.
The hard part is understanding the problem deeply enough to know what to build. It is turning implicit knowledge into explicit rules, identifying the assumptions hiding inside apparently obvious requirements, designing for what happens outside the happy path, and deciding where automation is safe and where human judgment still matters.
Ultimately, the difficult question is not whether the system in front of you works.
It is whether you have enough reason to trust that it is telling you the truth.
Identity Hub has been my clearest example of that so far.
The code came fast. Trust took months.












