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

Glossary

The terms this app uses, written for mixed technical and product audiences. Risk flag chips anywhere in the app link straight to their definitions here.

Estate concepts

Estate
Everything Travel Counsellors deploys to production, traced from Azure DevOps release pipelines rather than tribal knowledge. The current estate is 181 repositories out of 661 in the Azure DevOps project.
Assessment run
A complete pass over the estate: every in-scope repository is assessed and the results are aggregated into one dataset with a run id (for example run-2026-07-24-1). Each run is an immutable snapshot, which is what makes change over time measurable.
Deployed repo trace
The automated walk from every release pipeline back through its build pipeline to the source repository. It decides which repositories are actually deployed to production and therefore in scope.
Processed / not processed / excluded
The ledger status of every repository in Azure DevOps. Processed: assessed in the latest run with full detail available. Not processed: either in scope but not yet assessed, or not linked to any release pipeline. Excluded: deliberately out of scope, with the reason recorded (testing, deprecated, tooling, or no production use).
Fan-in and fan-out
Fan-in is how many other systems depend on a repository; fan-out is how many it depends on. High fan-in makes a component a chokepoint: changes to it ripple widely. High fan-in is not automatically bad, but it raises the stakes.
Binding point
One of the ten places where the estate's entanglement is anchored: four pinning components, two frozen contract packages, three core shared databases, and WCF as a transport. Cutting a binding point releases whole populations of systems at once.
Pinning component
A component that mechanically prevents its consumers from leaving the legacy platform, either because it only speaks WCF or because it is frozen on an old framework. Distinct from a well-maintained chokepoint like tc-connectionbroker, which has high fan-in but already supports modern .NET.
Kernel, ring and loose
The three-way partition of the estate by how many binding mechanisms apply to each system. Kernel: two or more mechanisms, the entangled core that moves in groups. Ring: exactly one mechanism, released as a by-product when its single thread is cut. Loose: no mechanisms, needing no untangling work. The partition is computed by rule from the dataset, so counts can differ slightly from the whitepaper narrative.
Shared database
A database read or written by more than one system. Sharers are coupled even without any code dependency: schema changes force them to move together (in convoy). The three core shared databases are Phenix, EventLog and ContactCentre.
Convoy
What a shared database imposes: every system sharing the schema must coordinate and move together when the schema changes, whether or not they share any code.
Capability family
A group of systems that implement the same business capability more than once, for example four separate ways of sending email. Families are consolidation opportunities: keep a survivor, retire the duplicates.
Health score
A 0 to 100 score per repository computed from seven weighted factors: runtime currency (25), legacy transport (15), test posture (15), security hygiene (15), dangerous coupling (15), activity (10) and deployment hygiene (5). Green is 75 and above, amber 50 to 74, red below 50. The factor breakdown is always shown; there is no black box.
Modernisation priority
The assessor's judgement of how urgently a repository needs modernisation work (high, medium or low), recorded during the assessment. Complements the computed health score.
Dormant but deployed
A repository with no meaningful code change for two years or more that is still linked to a production release pipeline. Dormancy plus duplication is the cheapest risk to remove: retire instead of migrating.
Hosting (on-prem vs AWS)
Where a repo's deployed artefacts actually run, derived from its pipelines rather than documentation: deployment-group phases and IIS or Windows service tasks mean our own machines (on-prem); container builds shipped to Amazon ECS through the shared pipeline template and Terraform mean AWS. "AWS + on-prem" means the repo ships artefacts to both. "No signal" usually means the artefact is a package (a library), not a hosted service. Regenerate with scripts/derive-hosting.py.
Deployment group
An Azure DevOps concept: a named set of machines with agents installed, which release pipelines deploy onto directly. In this estate, deployment-group phases are the signature of on-prem hosting.
ZDD (zero-downtime deployment)
Deploying software during business hours without taking the platform down. At Travel Counsellors this is a delivered capability: the automated stealth-release process (Workflow Engine) ran its first fully automated no-downtime production deployment in mid-2026. The app classifies every repo's ZDD readiness on a scale from "no excuse" (nothing technical holds it on the three-week release) to "no chance" (the deliberately frozen legacy core).
Stealth release
A deployment made without platform downtime, historically manual and used for urgent fixes: drain a server from the load balancer, deploy, health-check, return it, repeat. Now automated end to end by the Workflow Engine.
Expand and contract
The database migration pattern that makes schema change safe while old and new code run together: add the new shape alongside the old (expand), migrate code and backfill data, and only then remove the old shape (contract). Destructive schema changes in a single release are prohibited under the ZDD standard.
Change freeze (frozen core)
A deliberate policy preventing new change flowing through the entangled legacy core (Phenix Back Office, Admin Studio, the core schema). Part of the two-speed model: protect the core on a slow scheduled cadence, and let everything else release independently.
Dual-version safety
The property that old and new versions of a system can run side by side during a rolling deployment: backwards-compatible APIs, tolerant consumers, compatible serialized data, and expand-and-contract schema changes. The WCF counter-example: adding a value to a shared WCF enum instantly breaks every consumer that has not been recompiled, which is why WCF hosts score poorly on ZDD readiness.
Programme stages 0 to 4
The recommended sequence from the whitepaper. Stage 0: rotate exposed credentials immediately. Stage 1: shrink the estate by retiring and consolidating. Stage 2: cut the binding points. Stage 3: run the WCF and .NET Framework exit as a coordinated programme. Stage 4 (parallel): treat shared databases as products with named owners. Plus a continuous rule: raise the test floor where fan-in is high.

Technology terms

WCF (Windows Communication Foundation)
A legacy Microsoft messaging technology used for service-to-service communication. It has no direct successor on modern .NET, which is why 97 systems touching WCF is the estate's biggest structural problem. Systems can be WCF hosts (they expose endpoints), clients (they call endpoints), or consumers of WCF contract packages.
ASMX / SOAP
ASMX is an even older Microsoft web service technology; SOAP is the XML messaging protocol both it and WCF speak. Like WCF, there is no path to run these on modern .NET.
.NET Framework vs modern .NET
.NET Framework (versions 4.x) is the Windows-only platform generation that left mainstream support years ago; 82 estate systems still run on it. Modern .NET (8, 10) is the supported, cross-platform successor. Moving between them is a replatforming exercise, not a version bump.
End-of-life (EOL) runtime
A runtime no longer supported by its vendor: no security patches, no fixes. In this estate that means .NET Framework 4.x, .NET Core 2.x/3.1, .NET 5/6/7 and Node 8.
Contract package
A NuGet package holding the shared interface definitions (contracts) between a service and its consumers. Two contract packages are frozen at .NET Framework 4.5.2, pinning every consumer to the old platform until they are re-published with modern targets.
Multi-targeting
Building a library for both old and modern .NET at once, so consumers on either platform can use it. Multi-targeting a pinning component is how it stops being a blocker without breaking anyone.
CI (continuous integration)
Automated build (and ideally test) on every change. Every assessed repository in the estate has CI; test coverage is the gap, not build automation.
MongoDB
A document database, and the default operational store for the estate's modern and cloud services: 66 repos use it across 85 named databases, mostly one database per service. That one-owner pattern is the decoupled shape the programme wants, in contrast to the shared SQL Server schemas that force systems to move in convoy.
DynamoDB
AWS's serverless key-value database, used by the payments platform services for tokens, channel configuration and payment persistence.
Redis and Redis Streams
Redis is an in-memory data store used across the estate for caching and sessions; Redis Streams doubles as a bespoke message bus, bridged to MassTransit and SQS. Shared Redis instances are a second coupling plane on top of the shared SQL databases.
Amazon ECS
AWS's container hosting service (Elastic Container Service). The estate's cloud-deployed systems build a container image, push it to a registry and run it on ECS, with the infrastructure defined in Terraform.
MassTransit / SQS
MassTransit is a .NET messaging framework; SQS is the AWS queueing service beneath it. Together they are the estate's modern, acceptable coupling mechanism, in contrast to WCF.

Risk flags

Every flag recorded by assessors in the current run, with how many repos carry it.

legacy-framework (78 repos)
Built on .NET Framework or a similarly aged framework generation that is out of mainstream support.
boilerplate-readme (59 repos)
The README is template boilerplate; the repository does not explain itself.
shared-db (54 repos)
Reads or writes a database that other systems also use, coupling them through the schema.
no-tests (44 repos)
No automated tests at all: any change is made without a safety net.
eol-runtime (42 repos)
Runs on a runtime that no longer receives security patches from its vendor.
dormant (40 repos)
No meaningful code change for two years or more while still deployed to production.
wcf-client (27 repos)
Calls at least one WCF endpoint, tying it to the legacy transport until that endpoint has a modern alternative.
packages-config (20 repos)
Uses the legacy packages.config NuGet format, a marker of an old project system that complicates migration.
plaintext-credentials (17 repos)
Credentials stored in plain text in configuration or source.
incomplete-migration (15 repos)
A migration (of framework, platform or successor system) was started and left part-way, so two generations run at once.
high-fan-in (14 repos)
Many other systems depend on it; change here ripples widely.
wcf (14 repos)
Hosts WCF endpoints; consumers cannot leave the legacy transport until this system offers a modern interface.
no-readme (13 repos)
No README at all; institutional knowledge required to work on it.
secrets-in-source (11 repos)
API keys, tokens or credentials committed to source control. Rotate them; assume they are compromised.
hardcoded-secrets (7 repos)
Secrets baked into code rather than configuration or a secret store.
wcf-dependency (7 repos)
Depends on a component that is only reachable over WCF.
low-test-coverage (6 repos)
Tests exist but cover little of the behaviour that matters.
thick-client (6 repos)
A desktop application (for example WinForms) rather than a web application, with its own distribution and update burden.
wcf-dependencies (6 repos)
Depends on components that are only reachable over WCF.
monolith (5 repos)
A single large deployable carrying many responsibilities that would separate cleanly into services.
windows-service (5 repos)
Runs as a Windows service, tying deployment to Windows hosts.
single-controller-service (3 repos)
An entire deployed service that contains one controller; a candidate for consolidation.
soap (3 repos)
Speaks the SOAP XML protocol, a legacy integration style with no modern .NET server story.
deprecated-release-pipeline (2 repos)
Its release pipeline is marked deprecated or experimental yet is still attached.
hardcoded-config (2 repos)
Configuration values baked into code rather than externalised.
hardcoded-content (2 repos)
Assessor-recorded flag: hardcoded content.
hardcoded-credentials (2 repos)
Credentials written directly into code.
high-fan-out (2 repos)
Depends on an unusually large number of other systems, making it fragile to changes elsewhere.
low-activity (2 repos)
Very little recent change; approaching dormancy.
wcf-client-coupling (2 repos)
Coupled to other systems specifically through WCF client calls.
wide-blast-radius (2 repos)
A problem here would affect a large share of the estate at once.
aging-internal-packages (1 repo)
Assessor-recorded flag: aging internal packages.
binary-formatter-serialization (1 repo)
Uses BinaryFormatter, a serialisation mechanism Microsoft has deprecated for security reasons.
central-config-coupling (1 repo)
Assessor-recorded flag: central config coupling.
classic-pipeline (1 repo)
Assessor-recorded flag: classic pipeline.
clickonce-distribution (1 repo)
Distributed to users via ClickOnce, a legacy Windows install mechanism.
credentials-in-config (1 repo)
Assessor-recorded flag: credentials in config.
db-as-queue (1 repo)
Assessor-recorded flag: db as queue.
deprecated (1 repo)
Assessor-recorded flag: deprecated.
desktop-app (1 repo)
Assessor-recorded flag: desktop app.
desktop-clickonce-style-app (1 repo)
Assessor-recorded flag: desktop clickonce style app.
drift-from-live (1 repo)
What is in source control is known or suspected to differ from what runs in production.
dual-data-store (1 repo)
Assessor-recorded flag: dual data store.
dual-deployment-variants (1 repo)
Assessor-recorded flag: dual deployment variants.
dual-stack (1 repo)
Runs old and new platform generations side by side in one repository.
eol-adjacent-runtime (1 repo)
Assessor-recorded flag: eol adjacent runtime.
eol-frontend-framework (1 repo)
The front end is built on a framework version past end of life (for example Angular 4 to 14 or Vue 2).
eol-runtime-risk (1 repo)
Assessor-recorded flag: eol runtime risk.
event-specific-app (1 repo)
Assessor-recorded flag: event specific app.
fake-data-default-config (1 repo)
Assessor-recorded flag: fake data default config.
hardcoded-business-rules (1 repo)
Assessor-recorded flag: hardcoded business rules.
hardcoded-feature-toggles (1 repo)
Assessor-recorded flag: hardcoded feature toggles.
hardcoded-personal-emails (1 repo)
Assessor-recorded flag: hardcoded personal emails.
hardcoded-service-identity (1 repo)
Assessor-recorded flag: hardcoded service identity.
hardcoded-user-list (1 repo)
Assessor-recorded flag: hardcoded user list.
heavy-internal-nuget-coupling (1 repo)
Assessor-recorded flag: heavy internal nuget coupling.
internal-framework-coupling (1 repo)
Assessor-recorded flag: internal framework coupling.
legacy-frontend-tooling (1 repo)
Assessor-recorded flag: legacy frontend tooling.
legacy-ui-host (1 repo)
Assessor-recorded flag: legacy ui host.
long-running-in-process-tasks (1 repo)
Assessor-recorded flag: long running in process tasks.
manual-certificate-rotation (1 repo)
Assessor-recorded flag: manual certificate rotation.
manual-deploys (1 repo)
Deployed by hand rather than through the pipeline.
manual-launch-distribution (1 repo)
Assessor-recorded flag: manual launch distribution.
manual-operational-runbook (1 repo)
Assessor-recorded flag: manual operational runbook.
manual-run-risk (1 repo)
Assessor-recorded flag: manual run risk.
minimal-tests (1 repo)
Assessor-recorded flag: minimal tests.
mixed-runtime (1 repo)
Assessor-recorded flag: mixed runtime.
mixed-runtimes (1 repo)
Assessor-recorded flag: mixed runtimes.
model-in-source (1 repo)
Assessor-recorded flag: model in source.
monorepo-complexity (1 repo)
Assessor-recorded flag: monorepo complexity.
multi-service-monorepo (1 repo)
Several deployable services live in one repository, complicating ownership and release.
multi-tenant-config (1 repo)
Assessor-recorded flag: multi tenant config.
no-active-maintenance (1 repo)
Assessor-recorded flag: no active maintenance.
no-direct-db-visibility (1 repo)
Assessor-recorded flag: no direct db visibility.
no-source-code (1 repo)
The repository does not contain the effective source (for example only binaries or scripts).
outdated-chromium (1 repo)
Assessor-recorded flag: outdated chromium.
partial-schema-coverage (1 repo)
Assessor-recorded flag: partial schema coverage.
partial-schema-in-source (1 repo)
Assessor-recorded flag: partial schema in source.
preview-runtime (1 repo)
Runs on a pre-release runtime version.
scaffold-only (1 repo)
Assessor-recorded flag: scaffold only.
seasonal-dormant (1 repo)
Only active during a seasonal campaign; dormant the rest of the year but still deployed.
secrets-in-repo (1 repo)
Secrets present somewhere in the repository history or files.
shared-cache-coupling (1 repo)
Assessor-recorded flag: shared cache coupling.
shared-client-multitargeting (1 repo)
Assessor-recorded flag: shared client multitargeting.
shared-db-consumers (1 repo)
Assessor-recorded flag: shared db consumers.
shared-templates-coupling (1 repo)
Assessor-recorded flag: shared templates coupling.
single-purpose-shim (1 repo)
Exists only to adapt one system to another; likely to disappear once either side modernises.
single-route-gateway (1 repo)
Assessor-recorded flag: single route gateway.
split-stack (1 repo)
Assessor-recorded flag: split stack.
sqlproj-legacy-tooling (1 repo)
Assessor-recorded flag: sqlproj legacy tooling.
stale-docs (1 repo)
Assessor-recorded flag: stale docs.
stale-pipeline-sdk-pin (1 repo)
Assessor-recorded flag: stale pipeline sdk pin.
tests-skipped-in-ci (1 repo)
Assessor-recorded flag: tests skipped in ci.
thin-repo (1 repo)
Assessor-recorded flag: thin repo.
thin-test-coverage (1 repo)
A small number of tests relative to the surface area.
third-party-engine-coupling (1 repo)
Assessor-recorded flag: third party engine coupling.
tight-coupling (1 repo)
Deeply intertwined with specific other systems, so they cannot change independently.
utf16-legacy-scripts (1 repo)
Assessor-recorded flag: utf16 legacy scripts.
vendor-locked (1 repo)
Functionally tied to a specific third-party vendor.
vendored-binaries (1 repo)
Assessor-recorded flag: vendored binaries.
wcf-contracts (1 repo)
Assessor-recorded flag: wcf contracts.
wcf-endpoint (1 repo)
Assessor-recorded flag: wcf endpoint.
wcf-soap (1 repo)
Assessor-recorded flag: wcf soap.
windows-bound (1 repo)
Assessor-recorded flag: windows bound.
windows-only (1 repo)
Assessor-recorded flag: windows only.
windows-scheduled-task (1 repo)
Assessor-recorded flag: windows scheduled task.
xdt-config-transforms (1 repo)
Assessor-recorded flag: xdt config transforms.