
The CIAM Migration Diary, Part 2. The rule we set ourselves, what it meant in practice, and where it did not quite work.
In Part 1 I mentioned one of the main rules we set ourselves for this migration: changing the CIAM platform should not require the bank to change all the systems that consume it.
The reason is fairly simple. If replacing CIAM means dozens of application teams have to change code, schedule releases and retest their applications, the migration becomes much bigger than an identity programme. Your timeline is then set by every system around you rather than by the platform you are replacing. Most CIAM replacements that never happen are killed by that arithmetic long before anyone evaluates a product.
So wherever reasonably possible, we decided the new platform should adapt to the existing estate rather than asking the estate to adapt to us.
That sounds straightforward. In practice it means discovering how many assumptions have built up around a platform that has been running for years. It also does not remove the need for every consuming application and service to complete full regression testing against the new platform.
One example was a set of values carried in the authentication token. In principle they are configuration. Some consuming systems had the existing values fixed inside them and explicitly checked incoming tokens against them. The new platform naturally generated different values, so those systems rejected an otherwise valid token.
We did not know any of that when we ran our first regression test against those consuming applications. We spent time debugging why every token we returned was being rejected before we went to the consuming team for help, and we should have asked sooner. There is a balance to strike between crying wolf and imposing on other people's time for a good reason, and we are still working out where it sits.
We had a choice. Ask every consuming system that did this to change, or configure the replacement platform to present the existing values. We chose the second, and built it into our tooling as a configuration option rather than treating it as a one off customisation.
That was the right decision, but it illustrates the compromise involved. We have configured the new platform to look like the old one, in order to avoid asking several teams to change a handful of hard coded values.
There is a line I found myself writing to one of the teams during all this, which is probably the honest summary of the whole approach.
Once it is hard coded, you have to treat it as a contract whether anyone intended it to be one or not.
Compatibility decisions are not temporary, and we were never going to manage a full CIAM replacement and a rewrite of every consuming application to match my own view of best practice.
We found something similar with token validation. Running the incumbent and replacement in parallel created compatibility requirements that would not exist if we were doing a single cutover. That gave us another dependency to manage during the transition period. Nothing particularly difficult, but it was another example of complexity being created by coexistence rather than by either platform individually.
The biggest thing I have learned about compatibility so far is this. It is not really about whether you can reproduce an interface. It is about deciding where you want the change to happen. Every time you protect a consuming application from change, you move that complexity somewhere else rather than removing it.
Sometimes that is clearly the right trade. We had one consuming platform that could not hold the token we were issuing, because it had a fixed internal size limit. Nothing was broken. The token was valid and the limitation in the consuming product was real. Waiting for that product to change was not a practical option, so we changed the token representation for that integration to remain within the consuming platform's constraint. Would I design it that way from scratch? No. Was it the sensible decision for a migration with a fixed timeline and a third party product in the middle? Yes.
There is another limitation to our rule which is less technical. Avoiding changes to consuming applications does not mean you can avoid the consuming teams. They still have to test, and where testing or configuration has to align with one of their releases, you are still working to their calendar. We removed much of the technical dependency. We did not remove the programme dependency.
That distinction also led to one of the things I got wrong.
My starting assumption was that asking a consuming team to change something would generally be expensive, so wherever possible we should absorb the change ourselves. That was too simplistic. On one occasion we had already spent time engineering around a value when the team that owned the system told me they could have changed it in minutes. I had priced their change at the cost of a change in general, without asking how difficult that particular change actually was.
That has changed how I look at these decisions. My default is still to protect consuming teams wherever possible, but before engineering around something, ask the team that owns it what changing it really involves. Sometimes the answer is months of governance, development and release planning. Sometimes it is five minutes. You cannot tell from the outside.
There are two things I still do not have a good answer to.
The first is what happens to all this compatibility afterwards. We have deliberately absorbed complexity into the new platform so the rest of the bank does not have to change as part of this programme, and I think that is right. But it does not make the old assumptions disappear. Somebody still has to maintain them, and at some point it may make sense to remove them. We have not eliminated the change. In some cases we have separated it from the migration.
The second is harder. The bank does not stop changing while we migrate it. Application teams continue releasing new versions of their systems, and those releases are naturally tested against the CIAM platform that is live at the time, which is the platform we are removing. So compatibility is not a static target. The estate can keep changing underneath you while you are trying to reproduce its behaviour on the replacement platform.
I am not convinced we have the perfect answer to that yet. Do you publish a contract and ask every team to test against it? Give them access to the replacement platform before they release? Rely on regression testing to catch the differences?
If you have done a migration like this and found a good answer, I would genuinely like to hear it.
Part 2 of The CIAM Migration Diary. Part 3 is about what surprised us once we started integrating the new platform into the existing estate, and why your API contract is often much bigger than your API.
Writer’s Overview
Ajit Gupta – Co-Founder & CEO, Midships
Ajit leads Midships Group’s transition from a specialist identity consultancy to a portfolio of autonomous, AI-native business units. He focuses on long-term business relevance through platform thinking, customer outcomes, and scalable operating models.
Short bio: Ajit is a strategic founder with deep expertise in IAM, platform delivery, and AI services, driving Midships’ expansion across Asia, the Middle East, and beyond.

