Estate analysis — through run-2026-07-27-1
Cross-repo themes derived from data/estate.json (all 213 in-scope repos assessed). Scope history: run-2026-07-24-1 assessed the 177 classic release-linked repos; the trace was then extended to follow build-YAML/AWS deployment signals (ECS/Terraform/Lambda via the shared InternalOSS/azure-pipelines-templates), adding 36 AWS-era repos assessed in run-2026-07-27-1, and to exclude repos deleted from or disabled in ADO git. Numbers below come from the front matter aggregated into data/estate.json and the CSV exports alongside it; regenerate with scripts/build-estate-dataset.py then scripts/export-estate-csv.py.
Estate shape
213 assessed repos: 102 APIs, 38 services, 28 web UIs, 18 jobs, 17 libraries, 4 databases, 6 other (thick clients and tooling). C# dominates; SQL, TypeScript and JavaScript follow, plus the estate's first Python service (tc-ml-margin). Two repos still contain VB.NET (travelcounsellors-net, intranet).
Modernisation priority: 41 high, 53 medium, 119 low. The low count is genuinely encouraging — over half the estate is already on .NET 8/10 with CI and at least some tests, and the newly-traced AWS estate is almost entirely modern (28 of 36 low priority, none high).
The AWS estate (new in run-2026-07-27-1)
36 repos deploy straight from build YAML to AWS with no classic release pipeline — invisible to the original trace. They cluster into payments (tc-payment-router hub plus five PSP adapter repos and the payment-components Stencil UI), the mail platform (tc-mail-orchestrator, -composer, -scheduler, -tracked, tc-mail-sender's successors), the public/myTC APIs (tc-public-*), documents (tc-document-platform, tc-copilot-document-analyser) and ML margin pricing (tc-ml-margin, tc-phenix-dynamicmargin). Mostly healthy .NET 8/10 on ECS/Lambda, but the batch adds real findings: committed secrets in phenix-destination-search (OpenSearch credentials), tc-mail-composer (Trustpilot keys) and tc-mail-tracked (Spotler/SFTP); EOL runtimes in six (paymentprovider-dummy, phenix-connectionbroker-api, tc-public-content-api, tc-public-quote-api on .NET 6, tc-ml-margin on Python 3.8, phenix-destination-search net472 remnant); a production-deployed empty Angular scaffold (tc-public-features-ui); a placeholder API returning hardcoded values (phenix-connectionbroker-api); and duplication ripe for consolidation (viewing-as-api vs useraccount-api, payment-components-v1 vs payment-components).
End-of-life stacks
The single biggest theme. 83 repos (39%) still run on .NET Framework (4.5.2 through 4.8.1), all out of mainstream support; a further 13 sit on EOL .NET Core 2.x/3.1, .NET 6, Node 8 or Python 3.8. The percentage fell from 46% only because the newly-traced AWS estate is modern — the absolute Framework count barely moved.
Concentrations worth planning around:
- WCF is the estate's connective tissue: 98 repos (46%) touch WCF as host, client or contract package. Retiring WCF requires coordinated moves across the phenix-* service layer (
phenix-feature-service,phenix-passenger-service,phenix-task-reminder-wcf,mail-service,mailtemplate-service,tax-service,vcc-service) and every consumer pinned to their contract packages. Frozennet452contract packages (transfers-provider-servicecontract,phenix-passenger-configuration-service-contract) block consumers from leaving .NET Framework. - ASMX/SOAP with no modern .NET path:
phenix-passenger-service(actively developed),phenix-contact-centre-wcf,tc-documentation(generates ATOL certificates on .NET 4.5.2 with iTextSharp 5.1). - EOL front ends: Angular 4–14 SPAs still deployed (
phenix-post-booking,phenix-zendesk-forms,phenix-currency,phenix-vcc,tc-redis-cache-event-viewer), Vue 2 inphenix-destination-management, and Web Forms/MVC 4-5 sites (travelcounsellors-net,intranet,phenix-task-reminder-web).
Coupling hotspots
Fan-in from internal_dependencies (see data/dependencies.csv):
| Repo | Dependents | Notes |
|---|---|---|
| tc-connectionbroker | 126 | Central connection/config choke point for over half the estate |
| tc-exceptions | 77 | Recently multi-targeted net462/net8/net10, but its only test is an empty placeholder |
| backoffice-useraccount-service | 40 | User identity for the back-office estate |
| application-framework | 35 | Legacy Phenix application framework |
| phenix-feature-service | 28 | WCF-only feature flags on net472, no tests |
| company-info-api | 27 | Company/branding lookups |
| mail-service / mailtemplate-service | 21 / 18 | WCF mail path everything still routes through |
| phenix-user-api | 18 | Reaches into shared UserAccount and Phenix DBs |
| backoffice | 18 | The back-office monolith itself |
Any framework migration plan should start by making these nine safe to consume from modern .NET — several already multi-target, but phenix-feature-service and the mail pair are WCF-only and effectively pin their consumers.
phenix-retail is the largest single coupling consumer (25+ internal dependencies, a 285-package packages.config), half-migrated to phenix-booking-ui.
Shared data stores
shared-db is flagged on 54 repos. Store-level coupling from data/data-stores.csv:
- sqlserver:Phenix — 22 repos write to or read from the core Phenix database; its schema lives in
phenix-databaseas an append-only change ledger with table DDL only reconstructable from history. - sqlserver:EventLog — 14 repos, sqlserver:ContactCentre — 9, sqlserver:TravelCounsellors/tcdb — 7, sqlserver:UserAccount — 3, sqlserver:VirtualCreditCard — 3 (
vcc-serviceowns it;vccprovider-enettandvccprovider-wexwrite to it directly). - Redis is a second coupling plane: phenix-cache (12 repos), remote-session (9), streams (7), shared-cache (7), tc-cache (5). Redis Streams doubles as a bespoke message bus bridged to MassTransit/SQS by
tc-streams-masstransit-adapter.
Repos sharing a store are coupled even without a code edge; the Phenix DB group in particular moves as one unit for schema change.
Duplicated capabilities
Strong consolidation candidates surfaced by comparing purposes:
- Mail:
mail-service+mailtemplate-service(WCF) vsphenix-notifications-api(built to replace them but still delivering through the WCF client) vs the AWS mail platform (tc-mail-orchestrator/-composer/-scheduler/-trackedplustc-mail-sender). The AWS platform is clearly the successor; the question is a retirement plan for the WCF pair. - User identity:
useraccount-api,viewing-as-api,backoffice-useraccount-service,backoffice-useraccount-legacy,mytc-user-serviceandphenix-user-apiall serve user-account data;viewing-as-apiis largely duplicated byuseraccount-api(same Mongo databases, near-identical models). - Payment UI components:
payment-components-v1is a frozen duplicate ofpayment-componentskept deployed for unmigrated consumers. - Feature flags:
tc-features(dormant since 2019) vsphenix-feature-servicevs hardcoded company-GUID toggle maps inphenix-flights,phenix-retail-configuration-serviceandphenix-passenger-configuration-service-host. - Contact centre: five deployables across three generations (
phenix-contact-centre-wcf,-web,-api,-2-api,-2-web) plusphenix-contact-api, all over the shared ContactCentre DB. - Location:
phenix-locationservice-api(dormant, plaintext creds) vsphenix-place-repository(modern successor). - PSP adapters:
cybersource-api-integrationandtrustpayments-api-integrationare near-duplicates of the same pattern. - VCC:
vcc-service's WCF host calls eNett SOAP directly, duplicatingvccprovider-enett; thephenix-vccAngular app is duplicated inside thephenix-uimonorepo. - Exceptions packaging:
tc-exceptionsandwebapi-exceptionsboth publishTC.Exceptions.*packages; dependency edges currently map totc-exceptionsby convention.
Health and hygiene
- Tests: none in 47 repos, some in 95, good in 71. Worst where it matters most:
phenix-feature-serviceandtc-exceptionsare high fan-in with no real tests;tc-permissions-apiis authorisation-critical with ~29 tests. - Secrets: 36 repos have credentials or API keys committed to source (plaintext SQL credentials, PSP/OpenAI/social API keys, ClickOnce signing certificates). Standouts needing rotation:
public-api(production SQL + social keys),tc-copilot(live-looking OpenAI/TrustYou/Planisto keys),tc-useraccount-telephonecontracts-api(production SQL creds in every environment file),vccprovider-conferma(live Conferma API key in DB change scripts), plus the AWS-era findsphenix-destination-search(OpenSearch),tc-mail-composer(Trustpilot) andtc-mail-tracked(Spotler/SFTP). - Dormant but deployed: 40 repos have had no commit for 2+ years yet remain release-linked, the oldest untouched since 2017 (
backoffice-documentation-ews). Thetc-redis-cache-event-socket-server/-viewerpair is dormant since 2018 and hardcodes a dev hostname — likely broken in production. - Pipeline debt: retired or superseded repos still release-linked (
tc-swagger-versioninghas an in-repoRETIRED.md), deprecated and[experimenting-dont-use]pipeline sets still attached (phenix-cruise-service,phenix-reviews,phenix-itineraries,tc-phenix-cancellation).
Where to focus
- Unpin the core libraries —
tc-connectionbroker,tc-exceptions,application-frameworkand the mail pair gate every other migration; make each consumable from modern .NET (or replaceable) first. - Retire before rewriting — the retirement candidates (dormant + duplicated:
phenix-vcc,phenix-post-booking,phenix-zendesk-forms,tc-features,tc-redis-cache-event-*,phenix-retail-configuration-service,tc-swagger-versioning,payment-components-v1,viewing-as-api,tc-public-features-ui,phenix-connectionbroker-api) shrink the Framework surface and the AWS sprawl cheaply. - Treat shared databases as products — the Phenix, ContactCentre and EventLog DB groups need owners and declarative schema before service-level modernisation can proceed safely.
- Rotate committed secrets — 36 repos, prioritising the live production credentials called out above.
- Plan WCF exit as a programme, not per-repo tickets — 98 repos are touched; sequence by the fan-in table.