Skip to content

Planning a Platform Migration

Lesson 7 of 8Advanced16 min readGit at Scale & Enterprise Engineering · Repository FleetVerified: GitHub Enterprise Importer documentation, September 2026

Migrations do not fail. They stall — at eighty percent, with the difficult twenty percent unmigrated, two platforms running, and nobody able to say when it will finish.

Planning is mostly about the tail: identifying it early, scoping it deliberately, and being willing to declare parts of it out of scope rather than letting it consume the project indefinitely.

Inventory first. You cannot plan a migration of an estate you have not counted.

Reduce the scope before starting. Archived repositories are not migrated, and every one removed is work you do not do.

The repository migration is the small part. Secrets, environments, runners, teams, LFS and CI conversion are the bulk.

Migrate by team in waves, not by repository, so each team’s configuration work happens once.

Plan the tail explicitly. The last twenty percent takes as long as the first eighty, and the plan should say what happens to the parts that cannot move.

Define done. Not “repositories migrated” — “teams shipping from the new platform, old platform decommissioned”.

Before any of the planning below is worth doing, the project needs a position that survives month ten.

A stated outcome. What is true afterwards that is not true now, in a sentence somebody outside the platform team would recognise as valuable.

An agreed decommissioning date. Not aspirational — agreed with whoever pays for the source platform, so that continuing to run it is visibly a cost somebody bears.

Committed capacity from the teams, not just from the platform team. Every team will spend days on their wave, and that must be scheduled rather than assumed.

An executive sponsor who has already agreed the date and will hold it when a team asks for an extension in month nine.

A budget for the tail. The last twenty percent needs dedicated effort, and a project whose funding assumes linear progress will run out exactly when the hard part starts.

Permission to declare things out of scope. Without it, every difficult repository becomes an open item and the project cannot close.

Get these before starting. A migration begun without them is the one that stalls, and the stall is organisational rather than technical — the tooling works fine, and nobody has the authority to finish.

Enumerate everything. Repositories, their sizes, their activity, their owners, their CI configuration, their LFS usage, their secrets and environments.

Then triage into four categories:

Archive, do not migrate. Dead repositories. Frequently a third of the estate, and the cheapest reduction in scope available. See repository lifecycle.

Migrate straightforwardly. Active, within limits, standard CI. The bulk, and the easy part.

Migrate with remediation. Exceeds a size limit, uses LFS heavily, or has unusual CI. Each needs work before it can move.

Do not migrate. Repositories that genuinely cannot or should not move. Name them, state why, and decide what happens to them — this category always exists and pretending otherwise is how migrations stall.

The triage is the plan. Its category counts determine the timeline, and producing it is the first two weeks of the project.

The repository migration is minutes per repository and is not the constraint.

The configuration gap is hours to days per repository. Secrets, environments, runners, teams. This scales with repository count and it is the dominant cost. See GitHub Enterprise Importer.

CI conversion, from a non-GitHub source, is days to weeks per pipeline. Frequently larger than everything else combined, and frequently omitted from the plan because the migration tooling does not mention it.

LFS content migration scales with data volume rather than repository count, and can be substantial for a media-heavy estate.

Size-limit remediation is a project per affected repository, involving history rewriting and its coordination.

Verification is per repository, and must be automated to be feasible.

Communication and coordination — the largest omission in most plans. Every team must be engaged, scheduled, prepared, and supported through their wave.

Multiply your first estimate. Migration plans are optimistic in a consistent direction, and the reason is always the same: the configuration gap is invisible until you look at a specific repository.

A migration is the only cheap opportunity to change how the estate is organised, and skipping the question means inheriting the source’s structure by default.

Do not mirror the source. The organisation structure that accumulated on the old platform is unlikely to be the one you would choose, and recreating it is a decision made by omission.

Decide the target structure first, using the reasoning in enterprise account architecture. Usually fewer organisations than the source has.

Define the metadata schema before migrating, so repositories are classified as they arrive rather than in a subsequent campaign. See custom properties.

Configure the destination fully — rulesets, policies, teams, organisation settings, shared workflows — before the first wave. A repository arriving into a governed destination is governed; one arriving into an empty organisation needs remediation later.

But do not over-reach. A migration that also reorganises teams, changes the branching strategy, introduces new CI standards and adopts a new identity model is four projects, and it will fail as one.

Pick one improvement to bundle. Governance-on-arrival is usually the right one, because it costs almost nothing extra and it is much more expensive to retrofit.

Everything else can wait. A migration that completes and is followed by improvements beats an ambitious one that stalls.

Migrate by team, not by repository. A team’s repositories move together, their secrets are recreated in one session, their runners are re-registered once, and their verification is one exercise.

Pilot with a willing team that has representative complexity and will tell you what went wrong.

Then start with well-organised teams, whose migrations go smoothly and who become advocates.

Save the complicated ones for the middle, when the process is well understood but there is still time.

Do not save the hard ones for the end. That guarantees the project’s final phase is its most difficult, at the point when attention and budget are lowest.

Size waves so a team’s disruption is one short window, not a month of partial migration.

Leave gaps between waves for the team you just migrated to raise problems and for you to fix the process.

For an estate of 1,200 repositories across 40 teams, migrating from a non-GitHub source. Illustrative, and the shape is what matters rather than the numbers.

Weeks 1–3: inventory and triage. Enumerate everything, categorise, produce the plan. Output: 380 to archive, 610 straightforward, 170 needing remediation, 40 out of scope.

Weeks 2–8: archival. Runs in parallel. Notify owners, wait, archive. Removes a third of the scope. See repository lifecycle.

Weeks 3–6: destination setup. Organisations, teams, properties, rulesets, shared workflows, apps. Done once and it makes every subsequent wave cheaper.

Weeks 5–9: pilot. One willing team, full end-to-end, including CI conversion. Produces the runbook and the per-repository checklist.

Weeks 8–14: remediation. Size-limit fixes and LFS preparation, running in parallel with early waves.

Weeks 10–40: waves. Roughly one to two teams a week, allowing for holidays and release freezes. This is the bulk of the elapsed time and it is dominated by CI conversion and by team availability rather than by tooling.

Weeks 30–44: the tail. The difficult teams and the remediated repositories. Overlaps the waves deliberately, with a named owner.

Weeks 44–52: stabilisation. Problems surface and are fixed. No new waves.

Week 52: source read-only. Announced from week one.

Week 64: decommission. Twelve weeks of read-only retention.

Roughly fifteen months, of which the repository migrations themselves account for a few days of machine time. That ratio is the point.

The last twenty percent is where migrations stall, and it is predictable.

What is in it: repositories exceeding limits, teams that will not engage, systems with unusual CI, repositories nobody owns, and integrations nobody can explain.

Identify it during triage, not when you reach it. Every one of these categories is visible from the inventory in week two.

Assign it explicitly. The tail needs a named owner and dedicated time, not the residual capacity of a project winding down.

Be willing to declare items out of scope. A repository that cannot migrate, archived on the source platform with a documented reason and a retention period, is a resolved item. One that remains “to be migrated” indefinitely is what keeps two platforms running.

Set a decommissioning date for the source platform, early and publicly. It converts an open-ended tail into a deadline, and deadlines are what get the last teams to engage.

CI conversion, when the source is not GitHub

Section titled “CI conversion, when the source is not GitHub”

The largest item in most non-GitHub migrations, and the one most often absent from the plan.

Pipelines do not translate. Azure DevOps pipelines, GitLab CI configuration and Bitbucket pipelines all express similar ideas in incompatible ways, and no tool converts them faithfully.

Which makes this a rewrite per pipeline, and the cost scales with pipeline complexity rather than with repository count. A simple build is an hour; a multi-stage deployment pipeline with environments and approvals is a week.

Do not convert one-to-one. The migration is the opportunity to move to shared workflows. A team whose pipeline becomes five lines calling a central workflow has a better outcome than one whose four-hundred-line pipeline was faithfully reproduced.

Which changes the arithmetic favourably. Building three or four shared workflows covering the common patterns, and converting most teams to call them, is far less work than converting every pipeline individually — and it leaves the estate in a much better state.

Identify the patterns during triage. How many distinct pipeline shapes exist? Usually far fewer than the number of pipelines, because teams copied from each other.

Convert the shared workflows first, before the waves start, so each team’s conversion is a matter of calling one.

Budget the exceptions. Some pipelines genuinely do not fit a shared workflow, and those are individual projects. Identify them early; they belong in the tail.

And test conversions properly. A converted pipeline that builds but does not deploy correctly is discovered in production, and it is the failure that damages confidence in the whole migration.

Announce the migration, the reason and the timeline at the start, to everybody.

Give each team their date well ahead, with what will be required of them.

Be specific about their work. “You will need to provide the values for these eleven secrets” is actionable; “we will need your help with configuration” is not.

Publish a runbook covering what changes for a developer: the new URLs, the remote update, the credential setup, what to do when something does not work.

Have support available during each wave. A team hitting a problem with nobody to ask will conclude the migration was done to them badly.

Report progress publicly. Repositories migrated, teams completed, waves remaining. Visible progress sustains a long project.

And say what will be lost. Workflow run history, contribution attribution for departed colleagues, anything else. People accept losses they were told about and resent ones they discover.

Not “all repositories migrated”. A migration where the repositories moved and teams still use the old platform for something is not finished.

Done is: every team shipping from the new platform, the configuration gap closed for every migrated repository, the out-of-scope items resolved with documented decisions, all references updated, the source platform read-only, and a decommissioning date reached.

Add a stabilisation period. Two or three months after the last wave, during which problems surface and are fixed, before declaring completion.

And decommission. A source platform left running “just in case” indefinitely is the migration’s cost continuing forever, and it is the most common way a successful migration remains an ongoing expense.

The most underestimated part of a migration: everything outside the repository that points at it.

Other repositories’ workflows that check it out, call its reusable workflows, or reference it as a template. A fleet-wide search finds these, and each is a change somebody must make.

Deployment systems configured with repository URLs.

Documentation — internal wikis, runbooks, onboarding guides — full of links that will redirect and then, eventually, not.

Ticketing and project systems with repository references.

Monitoring and alerting that links to source.

External services with webhooks configured, or with the repository registered.

Package registries, where published packages record their source repository.

Developer machines, whose clones point at the old remote. Every engineer must update their remotes, which is a one-line command and a communication problem.

Bookmarks and muscle memory, which redirects handle for a while.

Inventory these during triage, not during cutover. A fleet-wide code search for the old platform’s hostname is the starting point and it typically returns far more than expected.

Redirects buy time and are not a solution. They work until the source is decommissioned, which is exactly when the unmigrated references break — and by then nobody is looking for them.

Fix references as part of each wave, so the work is distributed rather than accumulating into a final phase.

The organisational shape, because a migration is a programme rather than an engineering task.

A named owner with authority. Somebody who can say a repository is out of scope, extend a date, or escalate a team that will not engage.

A small core team. Two to four people running the tooling, doing the waves, and supporting teams. Larger does not help; the work is sequential per wave.

An executive sponsor. The decommissioning date will be challenged, and it needs somebody senior who has already agreed to it.

A weekly progress report. Waves completed, repositories migrated, tail items resolved, blockers. Visible, and to a wide audience.

A single channel for problems, monitored during waves.

A decision log. Every out-of-scope determination, every date extension, every exception. In two years somebody will ask why a repository is still on the old platform, and the log is the answer.

And explicit capacity from the teams being migrated. Their work — providing secret values, verifying pipelines, updating references — is real and must be scheduled with their management rather than assumed.

The most common organisational failure is a migration run as a side project by the platform team, with no sponsor and no team commitments, competing with delivery work. It stalls at exactly the eighty percent described above.

No inventory. Planning a migration of an unknown estate.

Not archiving first. Migrating repositories that should have been archived.

Sizing on repository migration time. The configuration gap is the cost.

Omitting CI conversion from a non-GitHub source. Frequently the largest item.

Migrating by repository rather than by team. Configuration work repeated.

Saving the hard teams for last. The final phase is the most difficult, with the least attention.

No source decommissioning date. The migration never finishes.

Not naming the out-of-scope items. They keep the project open indefinitely.

Declaring done at “repositories migrated”. Teams may still be on the old platform.

The risks worth naming in the plan, because each has a specific mitigation.

Data loss during migration. Mitigated by keeping the source intact and unmodified until each wave is verified. The source is the backup.

Extended freeze causing delivery impact. Mitigated by short per-wave windows and by doing configuration work before the freeze where possible.

A team unable to ship after their wave. The most damaging outcome, because it is visible and it colours perception of the whole project. Mitigated by making a successful end-to-end pipeline run the acceptance criterion, and by having support available.

Credential exposure during migration. Secrets are being handled, recreated and sometimes transported. Mitigated by using a secret manager throughout, never putting values in tickets or chat, and rotating anything that was handled insecurely.

The migration credential itself. Broad read on the source and broad write on the destination. Mitigated by a dedicated scoped identity, revoked immediately afterwards.

Silent verification failure. A migration reported as successful that was not. Mitigated by automated verification checking commits, branches, LFS and a real CI run.

The tail consuming the project. Mitigated by the decommissioning date and by willingness to declare items out of scope.

Loss of institutional knowledge. The person who knows what a secret is for leaves mid-project. Mitigated by capturing that knowledge during triage rather than at the wave.

Worth asking, because migrations are expensive and are sometimes proposed for reasons that do not survive examination.

Consolidating after an acquisition is the strongest case, and even there the question is whether the organisation must merge or merely come under the same enterprise account. Bringing an acquired organisation into the enterprise gets identity, policy and audit unified without moving a single repository — which is most of the benefit for a fraction of the cost. See enterprise account architecture.

Moving off an unsupported platform is a genuine forcing function.

Moving to access capabilities that do not exist on the source is legitimate, provided the capabilities are named and the value is real.

“Standardising on one platform” is a reasonable goal that should still be costed. Two platforms have a real ongoing cost — two governance configurations, two sets of tooling, two things to keep current — and the migration should be weighed against several years of that rather than against zero.

What is not a reason: a preference for the destination’s interface, a general sense that consolidation is good, or a strategic initiative with no stated outcome.

And partial migration is sometimes right. Moving the active estate and leaving an archived read-only source in place indefinitely, at low cost, is a legitimate end state — it avoids the tail entirely by declaring it out of scope up front.

Write down the outcome you expect. If it cannot be stated in a sentence that somebody would recognise as valuable eighteen months later, the migration will lose support around month ten, which is exactly when it needs it most.

A migration is a fleet-wide coordination project with a small technical core. The repositories move quickly; the configuration, the conversion, the coordination and the tail are the project. It finishes when the source platform is switched off, and it will not finish unless somebody set that date at the start.

  • Inventory and triage into archive, straightforward, remediate and out-of-scope is the first two weeks and produces the plan
  • Archiving before migrating is the cheapest scope reduction available
  • The repository migration is minutes; the configuration gap is the dominant cost
  • CI conversion from a non-GitHub source is frequently the largest single item
  • Migrate by team in waves so configuration work happens once per team
  • The tail is predictable from the inventory and needs a named owner and dedicated time
  • Declaring items out of scope, with documented reasons, is how the tail resolves
  • A source decommissioning date set at the start is what makes the migration finish
  • Done is teams shipping from the new platform and the source decommissioned, not repositories moved

Use an estate you know, or reason from documentation.

  1. Produce a repository inventory with size, activity, owner, CI configuration and LFS usage.

  2. Triage into the four categories. Predict: what fraction is archivable?

  3. For ten repositories in the “straightforward” category, list the configuration gap items. Estimate hours each.

  4. Multiply by the category count. Predict: how does that compare to your instinct for the project size?

  5. Identify the repositories that would be in the tail. Name the reason for each.

  6. Design the wave sequence: pilot, early, middle, and what you would not leave until last.

  7. Write the message a team would receive four weeks before their wave.

  8. Choose a source decommissioning date and justify it.

Engineering Team Onboarding SystemA 30-day Git and GitHub programme with standards templates, assessments and governance checklists.