{"id":1085,"date":"2025-11-06T15:03:28","date_gmt":"2025-11-06T15:03:28","guid":{"rendered":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?post_type=blog&#038;p=1085"},"modified":"2025-11-06T15:03:50","modified_gmt":"2025-11-06T15:03:50","slug":"lessons-from-swapping-out-our-authentication-system","status":"publish","type":"blog","link":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system","title":{"rendered":"Lessons from Swapping Out Our Authentication System"},"content":{"rendered":"\n<p class=\"undefined block-editor-paragraph\"><strong>TL;DR<\/strong> &#8211; We learned many lessons from swapping out our authentication system in Qt Maps SDK with the shared API used by the ArcGIS Maps SDKs for Swift, Kotlin, and Flutter. What looked like a targeted migration became a complex project involving technical, logistical, and usability challenges. By implementing the new authentication system in parallel alongside the legacy system, we were able to ship when we were ready, keep quality high, and de-risk the rollout.\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-backstory\">Backstory<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph\">If you\u2019ve been aware of ArcGIS Maps SDKs for Native Apps (formerly ArcGIS Runtime) for long enough, you\u2019ve likely heard us talk about our shared <strong>C++ Core <\/strong>(aka <em>Core<\/em> or <em>Runtime Core<\/em>); a single engine that powers multiple SDKs including Native Maps SDKs for .NET, Qt, Swift, Kotlin, and Flutter. Features are designed and implemented in Core, and each SDK team wraps Core to expose APIs using the conventions from their respective framework. Following this pattern keeps features and quality consistent while avoiding duplicated work. This architecture isn\u2019t without challenges, but it\u2019s been our secret sauce over the years for bringing ArcGIS to so many different platforms and frameworks, such as our recent roll-out of the brand new <a href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/developers\/announcements\/introducing-the-arcgis-maps-sdk-for-flutter\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter Maps SDK<\/a>.\u00a0<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"468\" height=\"346\" src=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/Architecture.png\" alt=\"\" class=\"wp-image-1086\" srcset=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png 468w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture-300x222.png 300w\" sizes=\"auto, (max-width: 468px) 100vw, 468px\" \/><figcaption class=\"wp-element-caption\">Architecture of ArcGIS Maps SDK for Native Apps <\/figcaption><\/figure><\/div>\n\n\n<p class=\"undefined block-editor-paragraph\">While the Core-first architecture works for most features, there are exceptions where an SDK team builds its own functionality outside of Core. Sometimes there are technical reasons, other times it\u2019s simply historical or resourcing related. Nevertheless, authentication was historically one of those outliers.\u202fThat changed in 2023 with the arrival of the new Native Maps SDKs for Swift and Kotlin. Faced with the prospect of writing two separate implementations for the new products, the group made a strategic decision: invest in a single, shared authentication API in Core.\u202fNot long after, the Native Maps SDK for Flutter joined the lineup. Thanks to the new Core API, it spun up its authentication system quickly, validating the decision. &nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Meanwhile, Qt Maps SDK had been using its same authentication system for nearly a decade. While it did the job, it was becoming challenging to add new features and fix bugs. Observing the successes of the Core innovations from the sidelines, our Qt team decided to align with this new pattern. This alignment would modernize our supported environments and workflows, reduce maintenance, improve quality, and unify behavior across Native Maps SDKs.&nbsp;&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Now that you&#8217;ve seen how the Native Maps SDKs team values code reuse and support for many platforms, let&#8217;s zoom into the Qt Maps SDK team and see how we migrated to a new authentication system. We embraced many different techniques, from elegant object-oriented programming patterns to less-desirable, but pragmatic copy-paste efforts.&nbsp;&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-setting-the-stage\">Setting the stage<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph\">We started planning in early 2024 with a major semantic release 300.0 on the horizon (targeting April 2026).&nbsp;&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\"><strong>The goal<\/strong>: Implement and release the new Authentication API by version 300.0.\u00a0<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">After an initial research spike and prototype, we quickly realized we could not make both systems work together, nor could we shim the old authentication system with the new system under the hood. While it would be great to avoid breaking changes for users, the two systems were too different, and we would regret merging the systems in the long run.\u00a0We could, however, have both systems exist side-by-side, allowing users to opt into the system of their choosing.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">With that initial information in place, we settled on the most important decision of the project: <strong>to build and release the new system alongside the legacy system<\/strong>. We then set forth the following principles:\u00a0<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Functional equivalence is critical<\/strong> &#8211; The new API must match legacy capabilities before it can be considered a viable replacement.\u00a0\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Don\u2019t compromise the long-term vision<\/strong> &#8211; The new API was our chance to reset. Temporary inconveniences, old behaviors, and historical oddities should not influence the new API.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Smooth migration path for users<\/strong> &#8211; Accepting that there would not be API compatibility, there must be a clear and simple upgrade path for users.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Plan for a clean removal<\/strong> &#8211; The new API would eventually replace the old API. Therefore we should have no shared dependencies that entangle the two systems.\u00a0\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Quality is nonnegotiable<\/strong> &#8211; Both systems must be production ready during the transition.\u00a0<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">With this vision in mind, we knew we could release the new API in phases and ultimately deprecate and remove the legacy API in version 300.0.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-mapping-the-road-ahead\">Mapping the road ahead<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph\">After reviewing the designs and scope of work from a high level, we sketched out an initial roadmap:&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"470\" src=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-11-05-at-2.27.10\u202fPM-1024x470.png\" alt=\"\" class=\"wp-image-1138\" srcset=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Screenshot-2025-11-05-at-2.27.10\u202fPM-1024x470.png 1024w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Screenshot-2025-11-05-at-2.27.10\u202fPM-300x138.png 300w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Screenshot-2025-11-05-at-2.27.10\u202fPM-768x352.png 768w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Screenshot-2025-11-05-at-2.27.10\u202fPM.png 1508w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Project roadmap, highlighting the primary focus areas for each release.<\/figcaption><\/figure>\n\n\n\n<p class=\"undefined block-editor-paragraph\">On paper, it was a clean, iterative plan. However, once we got started, the challenges started to amass.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-challenges\">Challenges<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-challenge-1-naming-nbsp-conflicts\">Challenge #1 \u2013 Naming&nbsp;conflicts<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">We wanted to keep canonical names (e.g., AuthenticationManager) in the new API. However, preserving names clashed with identically named types in the legacy API. C++ offers namespaces to scope and disambiguate classes, but our C++ API had always lived in a single namespace: <em>Esri::ArcGISRuntime&nbsp;<\/em><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\"><strong>Solution:<\/strong> introduce a dedicated Authentication namespace:&nbsp; <em>Esri::ArcGISRuntime::Authentication&nbsp;<\/em><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Simple, right? Unfortunately, not, and we broke our builds on Windows. How? Qt uses a concept called the Meta Object Compiler (MOC), to provide additional functionality not available in the C++ language by default (like signals and slots for events). The MOC runs as a preprocessing step of the build and generates additional files for each class with \u201cmoc_\u201d as a prefix. For example, <em>AuthenticationManager.cpp<\/em> would generate <em>moc_AuthenticationManager.cpp<\/em>. Next, the C++ compilation and linker steps execute, which then attempt to combine the compiled object files. Due to platform idiosyncrasies with the MOC on Windows, we ended up with duplicate symbols and a broken build, even though they were in different namespaces. We eventually worked through it with some clever build tricks. It was a good reminder that even \u201csimple\u201d naming decisions can spiral into unexpected technical challenges.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-challenge-2-preventing-mixed-use-nbsp\">Challenge #2 \u2013 Preventing mixed use&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">The legacy and new Authentication systems were designed to work independently. Importantly, developers must specify the desired system before the first network request goes out. If you intermix the two systems, unexpected bad things will happen. Unfortunately, early testing of our prototype showed that it was too easy to intermix the systems, even though we knew not to! Therefore, we decided we needed to improve the usability by adding two guardrails:\u00a0<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Folder separation<\/strong> \u2013 All headers for the new system would live inside an Authentication subfolder within our SDK\u2019s include directory. This guardrail gave developers a clear visual cue when adding classes to their projects, reinforcing the separation.&nbsp;<\/li>\n<\/ul>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-cpp\" data-lang=\"C++\"><code>\/\/ Legacy\n#include \u201cAuthenticationManager.h\u201d \n\n\/\/ New (Core-backed) \n#include \u201cAuthentication\/AuthenticationManager.h\u201d <\/code><\/pre><\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Build-time header guards<\/strong> \u2013 We added build-time checks that would break your build if you tried to mix the two systems. For example:&nbsp;<\/li>\n<\/ul>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-cpp\" data-lang=\"C++\"><code>\/\/ New System\n#define NEW_AUTH 1 \n #if defined(LEGACY_AUTH) &amp;&amp; !defined(DISABLE_AUTH_MIXED_USE_ERROR) \n #error(&quot;Cannot mix legacy and new Authentication systems. Define DISABLE_AUTH_MIXED_USE_ERROR to disable this error.&quot;)) \n#endif \n\n\/\/ Legacy System\n#define LEGACY_AUTH 1 \n #if defined(NEW_AUTH) &amp;&amp; !defined(DISABLE_AUTH_MIXED_USE_ERROR) \n #error(&quot;Cannot mix legacy and new Authentication systems. Define DISABLE_AUTH_MIXED_USE_ERROR to disable this error.&quot;)) \n#endif <\/code><\/pre><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"468\" height=\"98\" src=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/BuildError.png\" alt=\"\" class=\"wp-image-1087\" srcset=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/BuildError.png 468w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/BuildError-300x63.png 300w\" sizes=\"auto, (max-width: 468px) 100vw, 468px\" \/><figcaption class=\"wp-element-caption\">Build errors when including legacy and new authentication headers.<\/figcaption><\/figure><\/div>\n\n\n<p class=\"undefined block-editor-paragraph\">We considered runtime logging, but misuse often occurs before the first network request, so network logging could not catch this reliably. Compile time checks were reliable and effective, so we stuck with that.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-challenge-3-refactoring-the-networking-stack-nbsp\">Challenge #3 \u2013 Refactoring the networking stack&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">The naming and usability issues we tackled early on were important, but they were relatively contained. The\u202fbiggest\u202fchallenge that truly reshaped the project was realizing that the new authentication system wasn\u2019t\u202fjust\u202fan authentication overhaul.\u202fIt also introduced a\u202fnew HTTP network request pattern.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Previously, every individual object in our API would make its own network requests by connecting to a Core callback called RequestRequired. The callback from Core would fire when the API needed to make a request, and the API would issue the request and send the response back to Core. However, in the new system, all network calls would go through a single, stateless callback, referred to as the Global Request Handler. This new pattern was a major paradigm shift, and it fundamentally changed the way networking worked in Qt Maps SDK. We not only had to build new networking internals to handle the Global Request Handler, but we also needed to make the rest of the API adapt to support both the per-object and global request patterns. To accomplish this, we employed the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Strategy_pattern\" target=\"_blank\" rel=\"noreferrer noopener\">Strategy Pattern<\/a>, which allowed us to allocate the appropriate request system at runtime.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">In addition to supporting the two different request patterns, we now had lots of public API deprecations to make. Anywhere you could explicitly set a Credential object on a class would now be deprecated. This pattern could only be supported with the per-object request mechanism, so it would soon be obsolete.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">But, our problems didn&#8217;t end there! Our Portal API was mostly implemented at the API level. The strategy we put in place to support both the per-object and global request mechanisms worked for Core-backed features, but not for API-level features. We spoke with our colleagues and learned that there was an internal class designed for this scenario called ClientRequestSender. This class allowed us to ask Core to issue a network request on our behalf, which would then get routed through the Global Request Handler, ensuring API level requests would follow the same path as Core requests. Unfortunately, it also meant we needed to add extra logic for our entire Portal API. We ended up solving this through inheritance and polymorphism; each Portal class would have a subclass to handle the new client request pattern in an overridden virtual method.\u00a0<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-cpp\" data-lang=\"C++\"><code>\/\/ pre 200.8 implementation \nclass PortalGroupImpl \n{ \n public: \n void fetchGroupUsers() \n { \n \/\/ make the request \n m_networkManager-&gt;sendRequest(...); \n }; \n} <\/code><\/pre><\/div>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-cpp\" data-lang=\"C++\"><code>\/\/ new implementation (for legacy system) \nclass PortalGroupImpl \n{ \npublic: \n virtual void fetchGroupUsers() \n { \n \/\/ make the request on our own\n m_networkManager-&gt;sendRequest(...); \n }; \n} \n\n\/\/ new implementation (for new system) \nclass CorePortalGroupImpl : PortalGroupImpl \n{ \n public: \n void fetchGroupUsers() override \n { \n \/\/ Ask core to issue a request for us\n ClientRequestSender::sendRequestAsync(...); \n }; \n} <\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph\">As you can see, what started as a targeted authentication overhaul quickly became a full-blown networking stack refactor.&nbsp;&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-challenge-4-testing-nbsp-coverage\">Challenge #4 \u2013 Testing&nbsp;coverage<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Top-notch quality was a non-negotiable goal for us from the beginning. For the most part, things were going well initially, as we easily followed along the test designs written by the teams that had come before us. Our testing woes began once we fully understood the impact of the\u202fGlobal Request Handler\u202fand Client Request Sender mentioned earlier. Suddenly, we couldn\u2019t just add new test cases for the new authentication classes we were introducing. Now, every class that issued network requests, whether it used authentication or not, had\u202ftwo completely different code paths.\u202fThat meant we needed to double the coverage in our testing.\u202f&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">We faced a choice:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Option A: Get clever<\/strong>\u202f\u2013 Add hooks and conditionals into our tests and test runner so tests could run in both modes.\u00a0<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Option<\/strong> <strong>B:<\/strong> <strong>Duplicate<\/strong>\u202f\u2013 Copy and paste the tests, creating separate files for each mode.\u00a0<\/li>\n\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Against the team\u2019s initial instincts, we went with Option B for the following reasons:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The test name, code, and results in the Jenkins reports had clear expectations. This would simplify the daily task of monitoring test results and triaging failures.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Some tests needed slight code modifications, which would require a mess of #ifdefs littered in each test case if we didn\u2019t duplicate.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Duplicating would make it trivial to remove legacy tests later.&nbsp;<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"433\" src=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/Jenkins-1024x433.png\" alt=\"\" class=\"wp-image-1088\" srcset=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Jenkins-1024x433.png 1024w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Jenkins-300x127.png 300w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Jenkins-768x325.png 768w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Jenkins.png 1430w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Jenkins results for duplicated tests.<\/figcaption><\/figure><\/div>\n\n\n<p class=\"undefined block-editor-paragraph\">Yes, duplication is often a code smell, but the tests were clearer, more predictable, and more maintainable. The decision was pragmatic, and knowing that we only needed to live with the duplication for a few months made it more palatable. &nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-challenge-5-platform-specific-issues-nbsp\">Challenge #5 \u2013 Platform-specific issues&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">In the Native Maps SDKs group, we share more than just a common\u202fCore and API design. We also have shared test designs that specify test steps and data sources for all teams to implement.\u202fUnfortunately, Native Maps SDKs for Swift, Kotlin, and Flutter only run on\u202fiOS, Android, and macOS, while Qt Maps SDK also runs on\u202fWindows\u202fand\u202fLinux.\u202f That difference ended up being significant, as our new Integrated Windows Authentication (IWA) tests were all failing right out of the gate for Qt, but only when run on Windows.\u00a0<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">The problem? IWA can be configured in two modes:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>NTLM<\/em> (NT LAN Manager)\u202f\u2013 An older challenge\/response protocol.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>Kerberos\u202f<\/em>\u2013 A more modern Single Sign-On experience.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Both modes use the same credentials you use to log into Windows, but they behave differently. If our tests ran against a Portal configured with\u202fIWA Kerberos, no challenges would be issued, yet our test assertions expected them.\u202f&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">This mismatch didn\u2019t just break our tests\u2019 expectations; it broke our\u202fimplementation. Up until now, the entire design was based on a challenge\/response workflow. For example, we would issue a request to the server, the response would indicate that authentication was required, and we\u2019d forward the challenge on to the developer to handle. However, with Kerberos on Windows, Qt\u2019s network manager would see the <em>WWW-Authenticate: Negotiate<\/em> HTTP response header and automatically send the domain credentials back. As a result, our Qt Maps SDK doesn\u2019t even know a challenge or any authentication has taken place.\u202fWorking through this workflow difference took a lot of coordination with the\u202f.NET team\u202fand our\u202fauthentication experts\u202fto determine the right approach, but eventually, we found a solution.\u202fOnce we got our implementation working, we reworked the test designs to clearly indicate the differing expectations on Windows.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">This unexpected roadblock was an example how platform diversity can introduce subtle, but significant, technical challenges.\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-lessons-learned\">Lessons learned<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-parallel-implementations-were-the-right-call-nbsp\">1. Parallel implementations were the right call&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Looking back, building the new system alongside the legacy system was absolutely the right decision for us.&nbsp;Here\u2019s why:&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\"><strong>Separation of concerns<\/strong>\u202f\u2013 The new system stayed pure, free from legacy baggage. The legacy system stayed rock solid and unchanged, ensuring no new regressions.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\"><strong>Main branch integration<\/strong>\u202f\u2013 We could check the new API directly into the main branch, which meant:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We got working software building daily, which is an important tenant of Agile software development.&nbsp;&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We avoided long-running branches that required constant merges to stay in sync.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Other team members could start using the new API early, helping us gauge usability and stability.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We could write and run daily automated tests from the start.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If the API wasn\u2019t ready by release time, then removing it would be straightforward.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">This approach gave us flexibility, visibility, and confidence, without locking us into long running and isolated development.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-removing-a-foundational-system-is-never-trivial-nbsp\">2. Removing a foundational system is never trivial&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Even with careful planning, removing the legacy system from our\u202flarge codebase\u202fwas still a labor-intensive process. We learned that when you rip out a\u202f system that\u2019s been in your stack for over a decade, you will uncover oddities you never predicted, such as internal classes accessing the legacy API in unexpected ways.\u202f&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Our takeaway:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Do everything you can to make the removal simple.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Accept that it will still be complex, messy, and full of surprises.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Plan for extra time and patience, as it\u2019s not just a \u201cdelete and replace\u201d job.&nbsp;<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-3-plan-for-the-unknown-unknowns-nbsp\">3. Plan for the unknown unknowns&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">We did a risk assessment early on, but the number of\u202funforeseen issues\u202fwas still surprising, and they had a real impact on our deadlines.\u202f&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">For example, during 200.6, things were going well with the ArcGIS Authentication implementation, but we decided to play it safe and wait until 200.7 to go live. We believed that this would allow us to ensure the foundation that we laid for ArcGIS Authentication would work well for Network Authentication too. Unfortunately, in 200.7, we hit a wave of unexpected extra work (IWA on Windows issues, massive test duplication, and more).&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">In the end, we dedicated an entire release to cleaning up loose ends and didn\u2019t go live with the new authentication system until\u202f200.8.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Lesson learned: build in buffer time for the things you\u202fdon\u2019t\u202fknow you\u2019ll encounter.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-4-be-pragmatic-but-don-t-compromise-the-end-goal-nbsp\">4. Be pragmatic, but don\u2019t compromise the end goal&nbsp;<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Throughout the project, we faced technical challenges that could have derailed us.&nbsp;<br>For example:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Name clashes\u202f\u2013 We stuck to our goal of keeping the API pure, even though it meant ugly build \u201cfixes\u201d and potential confusion from having two AuthenticationManager\u202fclasses. Those pain points were temporary, but the benefits will last.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Test duplication\u202f\u2013 Copy-pasting tests felt wrong, but it was simple, achievable, and temporary. Six months later, when we removed them, we were glad we\u2019d taken this approach.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">A philosophy we live by on the Qt team is <strong>be pragmatic<\/strong>. After this project, I think a better articulation of our philosophy is: <strong>Be pragmatic\u2026 but don\u2019t compromise the end goal.<\/strong>&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph\">In the end, we delivered everything we wanted in 200.8, but some unexpected challenges hampered our ability to incrementally release features along the way like we initially hoped. The migration ended up being more than just a technical upgrade. It was a crash course in\u202fadaptability.&nbsp;We learned that:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Even the best-laid plans will encounter unknown unknowns.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pragmatism is essential, but the long-term vision must remain intact.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Parallel implementations can be a lifesaver when replacing a foundational system.&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Platform diversity can surface hidden complexities that only appear in real-world testing.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph\">Most importantly, we were reminded that swapping out our authentication system isn\u2019t just about writing code, it&#8217;s about\u202fmanaging complexity, making trade-offs, and keeping the team aligned with the project vision through uncertainty.&nbsp;<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph\">The new Authentication API in Qt Maps SDK is now cleaner, more maintainable, and better aligned with modern ArcGIS requirements. To learn more about the Authentication API in Qt Maps SDK, see the previous <a href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-qt\/developers\/introducing-next-generation-of-authentication-in-qt-maps-sdk\" target=\"_blank\" rel=\"noreferrer noopener\">ArcGIS Blog<\/a> that covers the user facing benefits, key differences between the legacy and new system, and suggested migration patterns. If you\u2019re interested in working on challenging, multi-platform, large scale software projects like this, take a look at our <a href=\"https:\/\/www.esri.com\/en-us\/about\/careers\/job-search?category=ArcGIS+Product+Engineering&amp;category=Software+Development+and+Engineering&amp;q=Maps+SDK\" target=\"_blank\" rel=\"noreferrer noopener\">careers site<\/a> and join the team!\u00a0<\/p>\n","protected":false},"author":23,"featured_media":0,"parent":0,"menu_order":0,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[74,51,73,49,72],"class_list":["post-1085","blog","type-blog","status-publish","format-standard","hentry","category-software-development","tag-architecture","tag-c","tag-migrating","tag-qt","tag-refactoring"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.2 (Yoast SEO v25.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Lessons from Swapping Out Our Authentication System<\/title>\n<meta name=\"description\" content=\"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Lessons from Swapping Out Our Authentication System\" \/>\n<meta property=\"og:description\" content=\"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system\" \/>\n<meta property=\"og:site_name\" content=\"Esri Software Engineering Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-06T15:03:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png\" \/>\n\t<meta property=\"og:image:width\" content=\"468\" \/>\n\t<meta property=\"og:image:height\" content=\"346\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system\",\"name\":\"Lessons from Swapping Out Our Authentication System\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/Architecture.png\",\"datePublished\":\"2025-11-06T15:03:28+00:00\",\"dateModified\":\"2025-11-06T15:03:50+00:00\",\"description\":\"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0\",\"breadcrumb\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png\",\"contentUrl\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png\",\"width\":468,\"height\":346},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Lessons from Swapping Out Our Authentication System\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/\",\"name\":\"Esri Software Engineering Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Lessons from Swapping Out Our Authentication System","description":"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system","og_locale":"en_US","og_type":"article","og_title":"Lessons from Swapping Out Our Authentication System","og_description":"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0","og_url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system","og_site_name":"Esri Software Engineering Blog","article_modified_time":"2025-11-06T15:03:50+00:00","og_image":[{"width":468,"height":346,"url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system","name":"Lessons from Swapping Out Our Authentication System","isPartOf":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage"},"image":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage"},"thumbnailUrl":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-content\/uploads\/2025\/10\/Architecture.png","datePublished":"2025-11-06T15:03:28+00:00","dateModified":"2025-11-06T15:03:50+00:00","description":"By implementing the new authentication system alongside the legacy system, we shipped when we were ready and de-risked the rollout.\u00a0","breadcrumb":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#primaryimage","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png","contentUrl":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/Architecture.png","width":468,"height":346},{"@type":"BreadcrumbList","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/lessons-from-swapping-out-our-authentication-system#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog"},{"@type":"ListItem","position":2,"name":"Lessons from Swapping Out Our Authentication System"}]},{"@type":"WebSite","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/","name":"Esri Software Engineering Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"text_date":"November 6, 2025","author_name":"Lucas Danzinger","author_page":false,"custom_image":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/banner2.png","primary_product":false,"tag_data":[{"term_id":74,"name":"architecture","slug":"architecture","term_group":0,"term_taxonomy_id":74,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":51,"name":"C++","slug":"c","term_group":0,"term_taxonomy_id":51,"taxonomy":"post_tag","description":"","parent":0,"count":2,"filter":"raw"},{"term_id":73,"name":"migrating","slug":"migrating","term_group":0,"term_taxonomy_id":73,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":49,"name":"Qt","slug":"qt","term_group":0,"term_taxonomy_id":49,"taxonomy":"post_tag","description":"","parent":0,"count":2,"filter":"raw"},{"term_id":72,"name":"refactoring","slug":"refactoring","term_group":0,"term_taxonomy_id":72,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"}],"category_data":[{"term_id":3,"name":"Software development","slug":"software-development","term_group":0,"term_taxonomy_id":3,"taxonomy":"category","description":"Articles focusing on how we apply coding practices, software design, development methodologies, and tools used while building our products, systems, etc.","parent":0,"count":8,"filter":"raw"}],"product_data":{"errors":{"invalid_taxonomy":["Invalid taxonomy."]},"error_data":[]},"primary_product_link":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/","short_description":"We modernized the Authentication API in the Qt Maps SDK by adopting the shared API used by the ArcGIS Maps SDKs for Swift, Kotlin, and Flutter","image":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2025\/10\/banner.png","_links":{"self":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article\/1085","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article"}],"about":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/users\/23"}],"version-history":[{"count":0,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article\/1085\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/media?parent=1085"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/categories?post=1085"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/tags?post=1085"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}