The Travel Counsellors platform estate: findings, risks and the path to modernisation
Consolidated technical whitepaper, platform modernisation programme
| Author | Chief Architect (platform modernisation) |
| Audience | Technology leadership; readable by semi-technical colleagues (for example product directors) |
| Status | Consolidated findings from the deployed-estate trace and the full estate assessment (run-2026-07-24-1, 177 of 181 in-scope repositories assessed) |
| Data sources | data/deployed-repos/latest/ and estate-assessment/ in the project-map repository, traced and assessed from Azure DevOps (uktc/TravelCounsellors) |
1. Executive summary
This paper consolidates two pieces of work: a trace of everything Travel Counsellors deploys to production, and a structured assessment of each of those systems. Together they replace tribal knowledge with an evidence-based picture of the platform estate, and that picture is now complete enough to plan modernisation from.
The headline findings are as follows.
- The production estate is 181 repositories, not the 661 that sit in Azure DevOps. After tracing every release pipeline back to its source code, 480 repositories turn out to be test assets, deprecated code, tooling or simply unconnected to any deployment. Of the 181 in scope, 177 were assessed; the remaining 4 no longer exist in Azure DevOps.
- Roughly half the estate is in good shape. 91 of 177 systems (51 per cent) are low modernisation priority, mostly already on modern .NET 8 or 10 with continuous integration and at least some tests. Every assessed repository has CI. The problem is concentrated, not universal.
- The other half runs on end-of-life foundations. 82 systems (46 per cent) still run on .NET Framework, a Microsoft platform generation that left mainstream support years ago, and 9 more sit on other end-of-life runtimes. Of the 41 systems rated high priority, 37 are on .NET Framework. Risk and runtime age line up almost exactly.
- The estate is stitched together by technology with no future. 97 systems (55 per cent) communicate through WCF, a legacy Microsoft messaging technology that has no direct successor on modern .NET. A handful of central components (a connection broker used by 103 systems, a shared exceptions library used by 71, a feature flag service and a mail pair that are WCF-only) effectively pin large parts of the estate to the old platform.
- The entangled core is real but bounded, and it is factorable. Cross-cutting the coupling data shows 70 systems (40 per cent) bound into the core by two or more binding mechanisms, 51 attached by exactly one, and 56 (32 per cent) not entangled at all. The entanglement itself runs through ten binding points (four legacy components, two frozen contract packages, three shared databases and WCF as a transport), so cutting those releases whole populations at once. Ten modern systems, including the payment gateway adapters and the API gateway, could be split out today with no dependency work.
- Databases couple systems as strongly as code does. 21 systems share the core Phenix database, 14 share the EventLog database and 9 share the ContactCentre database. These groups must move together during any schema change, and the Phenix schema itself is only reconstructable by replaying an append-only change ledger.
- There are immediate security and hygiene issues. 33 repositories (19 per cent) have credentials or API keys committed to source code, several of them live production credentials. 37 systems have had no code change for two or more years yet remain deployed, one untouched since 2017. Several capabilities exist in triplicate or quadruplicate, including four separate ways of sending email.
The recommended path forward, set out in section 7, is sequenced to shrink the problem before spending on it: rotate the exposed credentials immediately, retire the dormant and duplicated systems, unpin the small set of core libraries that gate everything else, then run the WCF and .NET Framework exit as a single coordinated programme ordered by dependency fan-in, with shared databases treated as products with named owners.
2. Background and purpose
Travel Counsellors is preparing a platform modernisation programme. Any such programme lives or dies on the quality of its inventory: without knowing what runs in production, what state it is in and how it is coupled, the work cannot be sized, sequenced or measured.
Historically that knowledge was tribal. The 661 repositories in Azure DevOps include everything the organisation has ever built or experimented with, with no metadata separating live systems from abandoned ones. This programme replaced that with two datasets, both regenerable on demand:
- The deployed-repository trace (24 July 2026), which followed every release pipeline in Azure DevOps back through its build pipeline to its source repository, classifying all 661 repositories as included (production), excluded (with a recorded reason) or not linked to any deployment.
- The estate assessment (run-2026-07-24-1), in which each in-scope repository was individually inspected and recorded in a structured, machine-readable format: purpose, technology stack, runtimes, data stores, internal and external dependencies, integration protocols, health signals and risk flags, concluding in a modernisation priority.
Two principles governed the work: scope is derived from deployment evidence rather than curated by hand, and every exclusion is recorded with a reason so decisions stay auditable and reversible.
3. Method in brief
The trace classified the 661 repositories as: 181 included (production applications), 90 excluded with recorded reasons (44 testing, 16 no production use, 15 infrastructure or tooling, 15 deprecated) and 398 not linked to any release pipeline. Assessment then ran across the included set in four waves; 4 repositories (admin-confirmation, backoffice-bookingview-api, phenix-rewards, phenix-transfers) no longer exist in Azure DevOps and are recorded as failed, leaving 177 assessed systems. All figures in this paper derive from the aggregated dataset (estate-assessment/data/estate.json) and the analysis and visualisation outputs generated from it.
4. The estate at a glance: scale and proportions
4.1 What the estate is made of
| Kind | Count | Share |
|---|---|---|
| APIs | 78 | 44% |
| Background services | 31 | 18% |
| Web user interfaces | 25 | 14% |
| Scheduled jobs | 18 | 10% |
| Shared libraries | 15 | 8% |
| Databases | 4 | 2% |
| Other (thick clients, tooling) | 6 | 3% |
C# dominates: 167 of 177 systems contain it, with SQL (38), TypeScript (17) and JavaScript (15) following. Two systems still contain VB.NET (travelcounsellors-net and intranet).
By product family: Phenix, the core travel platform, accounts for 65 systems; TC platform services for 30; Backoffice for 20; scheduled finance and operations jobs for 10; and 52 sit outside those families (payments, MyTC, CRM, content and various single-purpose services).
4.2 Where the risk sits
Each system was rated for modernisation priority: 41 high, 45 medium, 91 low. The proportions become meaningful when cross-cut by runtime generation:
| Runtime generation | High | Medium | Low | Total |
|---|---|---|---|---|
| .NET Framework (end of life) | 37 | 32 | 13 | 82 |
| End-of-life .NET Core, .NET 6 or Node 8 | 3 | 6 | 0 | 9 |
| Modern .NET 8 or 10 | 0 | 4 | 74 | 78 |
| Other | 1 | 3 | 4 | 8 |
The pattern is unambiguous. Not a single system on modern .NET is rated high priority, while 90 per cent of the high-priority systems sit on .NET Framework. Runtime modernisation and risk reduction are, for this estate, close to the same programme.
The high-priority population is also unevenly distributed by family: 8 of the 20 Backoffice systems are high priority, 19 of 65 Phenix systems, but only 2 of 30 TC platform services and none of the scheduled jobs. The back office is proportionally the most exposed part of the estate.
5. Findings: the problems in detail
5.1 End-of-life runtimes are the dominant theme
82 systems (46 per cent) run on .NET Framework versions 4.5.2 through 4.8.1, all outside mainstream support. A further 9 run on end-of-life .NET Core 2.x or 3.1, .NET 6 or Node 8. The two most common risk flags across the whole estate are legacy-framework (77 systems) and eol-runtime (36).
Specific concentrations that shape the migration plan:
- WCF is the estate's connective tissue. 97 systems (55 per cent) act as a WCF host, client or consumer of WCF contract packages. WCF has no direct equivalent on modern .NET, so this is the estate's largest structural obstacle. Worse, some contract packages are frozen at .NET Framework 4.5.2 (
transfers-provider-servicecontract,phenix-passenger-configuration-service-contract), which mechanically blocks every consumer from leaving the old platform. - ASMX and SOAP services with no modern path persist in actively developed systems:
phenix-passenger-service,phenix-contact-centre-wcf, andtc-documentation, which generates ATOL certificates (a regulatory document) on .NET 4.5.2 with a decade-old PDF library. - End-of-life front ends: Angular versions 4 through 14 remain deployed (
phenix-post-booking,phenix-zendesk-forms,phenix-currency,phenix-vcc,tc-redis-cache-event-viewer), plus Vue 2 inphenix-destination-managementand Web Forms or MVC 4-5 sites (travelcounsellors-net,intranet,phenix-task-reminder-web).
5.2 A small number of components pin the whole estate
Dependency fan-in (how many other systems depend on a component) identifies the chokepoints:
| Component | Dependent systems | Condition |
|---|---|---|
tc-connectionbroker | 103 | Central connection and configuration broker for over half the estate |
tc-exceptions | 71 | Recently multi-targeted for old and new .NET, but its only test is an empty placeholder |
backoffice-useraccount-service | 40 | User identity for the back-office estate |
application-framework | 34 | Legacy Phenix application framework |
phenix-feature-service | 25 | Feature flags, WCF-only, .NET Framework 4.7.2, no tests |
mail-service / mailtemplate-service | 21 / 18 | WCF mail path that everything still routes through |
company-info-api | 21 | Company and branding lookups |
phenix-user-api | 18 | Reaches into shared UserAccount and Phenix databases |
backoffice | 17 | The back-office monolith itself |
These nine components gate every other migration, though not equally: section 6 separates the true pins among them from the chokepoints that are already safe to consume from modern .NET. Several already support both old and new .NET, but phenix-feature-service and the mail pair are WCF-only and effectively pin their consumers to the legacy platform. On the consuming side, phenix-retail is the largest coupling consumer in the estate, with more than 25 internal dependencies and a 285-package legacy dependency manifest, and is already half-migrated to its successor phenix-booking-ui.
5.3 Shared databases couple systems invisibly
48 systems carry the shared-db risk flag. Systems that read and write the same database are coupled even when no code dependency exists between them, and must move together for any schema change:
- The Phenix database is shared by 21 systems, including the selling API, provider services, task reminders, the public API and the scheduled task runner. Its schema lives in
phenix-databaseas an append-only change ledger, meaning the current table definitions can only be reconstructed by replaying history. - EventLog is shared by 14 systems, ContactCentre by 9, TravelCounsellors/tcdb by 7, UserAccount and VirtualCreditCard by 3 each. In the VirtualCreditCard case, two provider integrations write directly into a database owned by
vcc-service, bypassing its API. - Redis is a second coupling plane: the phenix-cache instance is shared by 12 systems, remote-session by 9, and Redis Streams (7 systems) doubles as a bespoke message bus, bridged to the modern messaging stack by a dedicated adapter service.
5.4 Duplicated capabilities
Comparing system purposes surfaced capabilities built more than once, each a consolidation opportunity:
- Email, four ways: the WCF
mail-serviceandmailtemplate-servicepair;phenix-notifications-api, built to replace them but still delivering through the WCF client; andtc-mail-senderon a modern queue-based path. - Feature flags, three ways:
tc-features(dormant since 2019),phenix-feature-service(WCF-only), and hardcoded toggle maps inside three other services. - Contact centre, five deployables across three generations (
phenix-contact-centre-wcf,-web,-api,-2-api,-2-web) plusphenix-contact-api, all over the same shared database. - Location lookup, twice:
phenix-locationservice-api(dormant, plaintext credentials) and its modern successorphenix-place-repository. - Payment provider adapters:
cybersource-api-integrationandtrustpayments-api-integrationare near-duplicates of the same pattern. - Virtual credit cards:
vcc-servicecalls the eNett SOAP API directly, duplicatingvccprovider-enett; thephenix-vccfront end is duplicated inside thephenix-uimonorepo. - Exception packaging:
tc-exceptionsandwebapi-exceptionsboth publish packages under the same name family.
5.5 Health, security and hygiene
- Test coverage: 42 systems (24 per cent) have no tests at all, 79 have some, 56 have good coverage. The gaps are worst where they matter most:
phenix-feature-service(25 dependents) andtc-exceptions(71 dependents) have no real tests, andtc-permissions-api, which is authorisation-critical, has only around 29. Two actively released systems have no tests and three or more release pipelines, notablyphenix-provider-databaseswith 25. - Secrets committed to source: 33 repositories (19 per cent). These include plaintext production SQL credentials, payment provider and AI service API keys, and code-signing certificates. The most urgent rotations:
public-api(production SQL plus social media keys),tc-copilot(live-looking OpenAI and partner keys),tc-useraccount-telephonecontracts-api(production SQL credentials in every environment file) andvccprovider-conferma(a live payment provider key inside database change scripts). - Dormant but still deployed: 37 systems (21 per cent) have had no commit for two or more years yet remain release-linked. The oldest,
backoffice-documentation-ews, is untouched since 2017 and rated high priority. One dormant pair (tc-redis-cache-event-socket-serverand-viewer, untouched since 2018) hardcodes a development hostname and is probably already broken in production. - Pipeline debt: at least one formally retired system (
tc-swagger-versioning, which contains its ownRETIRED.md) is still release-linked, and several systems retain deprecated or explicitly experimental pipeline sets.
6. The entangled core, precisely: what binds the estate and what is already loose
"The estate is entangled" is true but unhelpful for planning. This section defines the entanglement exactly, using three measurable binding mechanisms, and partitions all 177 systems by how many of them apply. The partition is computed from the estate dataset and is regenerable, not impressionistic.
6.1 The three binding mechanisms
A system is bound into the core when one or more of the following applies:
- It depends on a component that pins it to the legacy platform. The pinning components are
phenix-feature-service(WCF-only feature flags, 25 dependents),mail-serviceandmailtemplate-service(WCF-only mail, 21 and 18 dependents),application-framework(34 dependents) and the two contract packages frozen at .NET Framework 4.5.2. A note of precision here: high fan-in alone is not entanglement.tc-connectionbroker, with 103 dependents the most depended-on component in the estate, already targets both old and modern .NET, has good tests and is actively maintained; it is a chokepoint to manage carefully, not a knot to untie. The same is increasingly true oftc-exceptions. The dangerous binders are the WCF-only services and the frozen contracts, because they mechanically prevent their consumers from leaving .NET Framework. - It speaks WCF, as host, client or consumer of WCF contract packages (97 systems).
- It shares one of the three core databases (Phenix, 21 systems; EventLog, 14; ContactCentre, 9), which forces schema changes to move in convoy.
6.2 The kernel: 70 systems bound by two or more mechanisms
70 systems (40 per cent) meet two or more of the criteria. This is the entangled core properly so called, and it is not evenly spread: it concentrates in the Phenix selling path (quote, flights, accommodation, retail and the selling API around the shared Phenix database), the contact centre stack (all generations, over the shared ContactCentre database), the back-office monolith and its satellites, and the WCF service layer itself (mail, feature flags, passenger, task reminder, exchange rates, tax). These systems cannot be modernised one ticket at a time; they move as groups, in the order set out in section 7.
6.3 The loosely attached ring: 51 systems held by a single thread
51 systems (29 per cent) touch exactly one binding mechanism, and the breakdown matters because each thread has a known cut:
- 35 are WCF-involved only, and 23 of those already run on modern .NET. These are typically modern services still calling one legacy WCF endpoint. They are not migration work in their own right: the moment the service they call exposes a modern interface (Stage 2 of the plan), they fall out of the core automatically.
- 8 share a core database only. They detach when the database group they belong to gains an owned API or completes its schema separation.
- 8 depend on a pinning component only. They detach when that component is multi-targeted or replaced.
6.4 What is already loose: 56 systems, 10 of them separable today
56 systems (32 per cent) meet none of the three criteria. Within this population:
- 10 are modern leaf systems: no other system depends on them, they share no data store and they run on .NET 8 or 10. They are, in effect, already split out:
asset-management,booking-data-exporter,cybersource-api-integration,trustpayments-api-integration,paymentprovider-br-dge,payment-check-service,job-ledger-export,tc-api-gateway,tc-auctionandtc-copilot. It is worth noting that the payment gateway adapters form a coherent, modern, self-contained payments edge; this is the estate's proof that clean separation is achievable within the existing organisation. - 12 are legacy leaf systems in the same structural position but on old runtimes, and most are dormant (for example
crm-reporting-api,html2pdf-service,tc-redis-cache-event-viewer). These split out by retirement rather than migration. - The remaining 34 couple only to each other, through modern mechanisms such as Redis Streams, MassTransit and SQS, or through minor shared stores outside the core three. This is ordinary, acceptable coupling between services, not entanglement, and it needs no untangling work.
6.5 What this means overall
Three conclusions follow.
First, the estate is healthier than its reputation, but bimodal. Half of it (78 systems on modern .NET, 74 of them low priority) needs no migration investment, only normal maintenance. The programme is therefore not a wholesale rebuild; it is the managed retirement of one platform generation.
Second, the entanglement is narrow at its root even though it is wide in its reach. 121 systems are touched by the core in some way, but the binding runs through ten points: four pinning components, two frozen contract packages, three shared databases and WCF as a transport. Cutting at those points releases the 51-system ring almost as a by-product and reduces the 70-system kernel to domain-sized groups. A repository-by-repository ticket queue would stall repeatedly on these same blockers; the dependency data says to cut the binding points first.
Third, some of the risk needs no migration at all to remove. 37 dormant systems, the duplicated capabilities and the exposed credentials can be addressed by retirement, consolidation and rotation, all far cheaper than replatforming, and doing so shrinks the kernel before the expensive work begins.
7. Migration options and the recommended path
7.1 Options for the legacy population
For each system on an end-of-life foundation there are four options, and the assessment data lets us assign a default to each system rather than debating case by case:
- Retire. For dormant systems whose capability is duplicated or no longer used. The dormant-but-deployed list and the duplication analysis identify the candidates (for example
phenix-vcc,phenix-post-booking,phenix-zendesk-forms,tc-features, thetc-redis-cache-event-*pair,phenix-locationservice-api,tc-swagger-versioning). This is the cheapest way to reduce the Framework surface. - Consolidate. Where a modern successor already exists, finish the migration and retire the predecessor: notifications over the mail pair,
phenix-place-repositoryover the location API,phenix-booking-uioverphenix-retail, one contact centre generation over three, one payment adapter pattern, one exceptions package. - Replatform to modern .NET. For systems that earn their keep, port to .NET 8 or later. For most ASP.NET APIs and services this is a well-trodden path. Where a WCF interface must survive temporarily, CoreWCF (the community-maintained port of WCF to modern .NET) is a viable transitional host, but the target state for service-to-service communication should be REST, gRPC or the existing message bus, not WCF preserved indefinitely.
- Leave for now. The 13 Framework systems rated low priority are stable, low-risk and not blocking anything. They migrate last, or when touched for other reasons.
Rewriting from scratch is deliberately absent as a default option. The half-completed phenix-retail to phenix-booking-ui migration and the three-generation contact centre stack are both evidence of what partial rewrites cost this estate; rewrites should be exceptional and case-argued.
7.2 Recommended sequence
The sequence below is ordered by dependency, not calendar. Each stage reduces the cost or risk of the next.
Stage 0, immediate and independent of everything else: rotate the exposed credentials. 33 repositories, prioritising the live production credentials named in section 5.5, then enable secret scanning in CI so the problem cannot silently recur.
Stage 1: shrink the estate. Execute the retirement and consolidation candidates. Every system retired here is a migration never paid for, a pipeline decommissioned and an attack surface removed. Disarm the release pipelines of retired and deprecated systems as part of the same pass.
Stage 2: cut the binding points. Make the chokepoint components consumable from modern .NET or replace them: complete the multi-targeting of application-framework, put real tests around tc-exceptions (71 dependents, one placeholder test), replace WCF-only phenix-feature-service with a modern feature flag service, complete the notifications migration to retire the mail pair, and re-publish the frozen .NET 4.5.2 contract packages with modern targets. This stage has the highest leverage in the entire programme: per the partition in section 6, it releases most of the 51-system loosely attached ring (including the 23 modern systems whose only legacy tie is a WCF client call) without touching those systems at all, and reduces the 70-system kernel to domain-sized groups.
Stage 3: run the WCF and Framework exit as a programme. With the ring released, what remains is the kernel: sequence its replatforming by fan-in (providers of contracts before their consumers), domain by domain, with the Backoffice family first on risk concentration and Phenix batched around its shared database groups. Track a single estate-level metric: systems remaining on end-of-life runtimes, currently 91.
Stage 4, in parallel from Stage 2 onwards: treat shared databases as products. Give the Phenix, EventLog and ContactCentre databases named owners, reconstruct declarative schema definitions from the change ledgers, and route the direct writers (for example the VCC provider integrations writing into vcc-service's database) through owned APIs. Service-level modernisation inside these groups is unsafe until this exists.
Continuous: raise the test floor where fan-in is high. Not a blanket coverage target, but a hard rule that the chokepoint components and authorisation-critical services carry good tests before they are changed.
7.3 Governance to keep the estate knowable
Three cheap measures protect the investment this analysis represents: archive deprecated repositories instead of renaming them (and remove their pipelines); introduce minimal repository metadata (owner, lifecycle status, production use) so the next inventory is a query rather than a project; and re-run the trace and assessment tooling on a regular cadence, diffing snapshots so drift is noticed when it happens. All the tooling for the third measure already exists and is checked in.
8. Limitations of the evidence
- The trace covers classic release pipelines; a system deployed exclusively through newer YAML pipelines would appear unlinked. The 181 scope is therefore a floor. Verifying YAML coverage remains an open action.
- Four in-scope repositories no longer exist in Azure DevOps and could not be assessed; they are recorded as failed in the run manifest.
- Assessment front matter is structured but human-authored from code inspection; individual fields may contain judgement calls. The dataset is versioned, so corrections are cheap and traceable.
- Release pipeline counts measure wiring, not deployment frequency; release history was not analysed in this run.
- This is a point-in-time picture (24 July 2026). The tooling regenerates the entire dataset, analysis and visualisations on demand.
9. Conclusion
The estate is now mapped end to end: 181 production systems traced from deployment evidence, 177 individually assessed, and the results aggregated into a dataset from which every figure in this paper is regenerable. The picture it shows is a bimodal estate: a modern half that needs nothing but maintenance, and an entangled core of 70 systems whose binding runs through ten identifiable points, surrounded by a 51-system ring held by a single thread each and 56 systems that are not entangled at all.
The recommendation to leadership is to authorise the sequence in section 7: rotate the exposed credentials now, shrink the estate through retirement and consolidation, cut the ten binding points to release the ring, and then run the WCF and Framework exit across the kernel as a single dependency-ordered programme with shared databases under explicit ownership. Every stage is justified line by line from the estate dataset, and progress is measurable against it.
Appendix A: data sources and reproducibility
All figures derive from version-controlled datasets in the project-map repository:
data/deployed-repos/latest/: the deployed-repository trace (summary.json,included.json,excluded.json,not-linked.json), regenerated byscripts/trace-deployed-repos.pyestate-assessment/repos/: 177 per-repository assessment records with machine-readable front matterestate-assessment/data/estate.json: the aggregated estate dataset built byscripts/build-estate-dataset.py, with CSV exports (repos.csv,dependencies.csv,data-stores.csv,risk-flags.csv)estate-assessment/analysis/estate-analysis.md: cross-estate analysis for run-2026-07-24-1estate-assessment/visualisation/: dependency graphs, the shared data store map, the stack and risk dashboard and correlation views, regenerated byscripts/build-estate-visualisation.py
The assessment process, roles and per-repository schema are defined in docs/platform-modernisation/ORCHESTRATION.md; programme context and decisions are recorded in docs/platform-modernisation/HANDOVER.md.