Platform Evolution
Assessment run-2026-07-27-1 · 213 of 213 repos assessed
Analysis / Whitepaper

The Travel Counsellors platform estate: findings, risks and the path to modernisation

Consolidated technical whitepaper, platform modernisation programme

AuthorChief Architect (platform modernisation)
AudienceTechnology leadership; readable by semi-technical colleagues (for example product directors)
StatusConsolidated findings from the deployed-estate trace and the full estate assessment (run-2026-07-24-1, 177 of 181 in-scope repositories assessed)
Data sourcesdata/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:

  1. 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.
  2. 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

KindCountShare
APIs7844%
Background services3118%
Web user interfaces2514%
Scheduled jobs1810%
Shared libraries158%
Databases42%
Other (thick clients, tooling)63%

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 generationHighMediumLowTotal
.NET Framework (end of life)37321382
End-of-life .NET Core, .NET 6 or Node 83609
Modern .NET 8 or 10047478
Other1348

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:

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:

ComponentDependent systemsCondition
tc-connectionbroker103Central connection and configuration broker for over half the estate
tc-exceptions71Recently multi-targeted for old and new .NET, but its only test is an empty placeholder
backoffice-useraccount-service40User identity for the back-office estate
application-framework34Legacy Phenix application framework
phenix-feature-service25Feature flags, WCF-only, .NET Framework 4.7.2, no tests
mail-service / mailtemplate-service21 / 18WCF mail path that everything still routes through
company-info-api21Company and branding lookups
phenix-user-api18Reaches into shared UserAccount and Phenix databases
backoffice17The 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-database as 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:

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) and tc-exceptions (71 dependents) have no real tests, and tc-permissions-api, which is authorisation-critical, has only around 29. Two actively released systems have no tests and three or more release pipelines, notably phenix-provider-databases with 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) and vccprovider-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-server and -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 own RETIRED.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:

  1. 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-service and mailtemplate-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 of tc-exceptions. The dangerous binders are the WCF-only services and the frozen contracts, because they mechanically prevent their consumers from leaving .NET Framework.
  2. It speaks WCF, as host, client or consumer of WCF contract packages (97 systems).
  3. 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:

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:

  1. 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, the tc-redis-cache-event-* pair, phenix-locationservice-api, tc-swagger-versioning). This is the cheapest way to reduce the Framework surface.
  2. Consolidate. Where a modern successor already exists, finish the migration and retire the predecessor: notifications over the mail pair, phenix-place-repository over the location API, phenix-booking-ui over phenix-retail, one contact centre generation over three, one payment adapter pattern, one exceptions package.
  3. 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.
  4. 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

  1. 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.
  2. Four in-scope repositories no longer exist in Azure DevOps and could not be assessed; they are recorded as failed in the run manifest.
  3. 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.
  4. Release pipeline counts measure wiring, not deployment frequency; release history was not analysed in this run.
  5. 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 by scripts/trace-deployed-repos.py
  • estate-assessment/repos/: 177 per-repository assessment records with machine-readable front matter
  • estate-assessment/data/estate.json: the aggregated estate dataset built by scripts/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-1
  • estate-assessment/visualisation/: dependency graphs, the shared data store map, the stack and risk dashboard and correlation views, regenerated by scripts/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.