A worked example. This is not a client case study. The company, estate, app counts, and architecture below are invented. The tool and the reasoning are real.

Picture a company that has been on Heroku for long enough that nobody is quite sure what is there anymore.

There are a bunch of apps. Some matter a lot. Some probably do not matter at all. Configuration lives partly in deploy scripts, partly in the Heroku dashboard, and partly in people’s heads. There is no useful infrastructure-as-code representation of the estate.

They want to move eventually.

Before planning the migration, I want to know what I am actually migrating.

The first problem is inventory

These should be easy questions:

  • What apps exist?
  • What process types does each app run, and at what sizes?
  • Which add-ons does each app use?
  • What custom domains are attached?
  • Which config variable names does each app expect?
  • Which apps are grouped into pipelines?
  • Which things are active, and which are leftovers nobody has touched in two years?

If answering those questions means clicking through dozens of dashboard pages, the estate is not really inventoried.

API access does not automatically fix that. Credentials may be able to read most of an account but not all of it. Some resources may return partial information. Some endpoints may simply refuse the token you were given.

That is useful information too. “I cannot read this with the current credentials” is much better than silently assuming there is nothing there.

Build a snapshot you can rerun

I would not start by writing a wiki page describing the Heroku environment.

The environment changes. The wiki page probably will not.

Instead, I want a machine-readable snapshot that can be regenerated whenever I need it.

I built an extension for swamp, a small tool I use for API-backed inventory work. Against Heroku, it can collect things such as:

  • applications
  • dyno formations
  • add-ons
  • custom domains
  • log drains
  • config variable names
  • pipeline relationships

The result is versioned and queryable.

Run it again next week and I can diff the two snapshots instead of asking everyone what changed.

Keep the shape, not the secrets

An inventory like this is much more useful if I can safely keep it in version control and hand it to other people.

That means deliberately not collecting values I do not need.

For example:

  • Config vars: store the variable names, never their values.
  • Log drains: store enough information to identify the drain, but not a URL containing credentials or tokens.
  • Add-ons: store the service, plan, and state, not connection strings.

For migration planning, I usually care that an application expects a database credential. I do not need the database password itself sitting in an inventory repository.

That distinction makes the snapshot much easier to use operationally. It can be committed, reviewed, diffed, and passed to whoever works on the migration next.

Now I can ask the estate questions

Once the information is structured, a lot of tedious discovery becomes ordinary queries.

Which apps use Redis?

Which custom domains exist?

Which applications gained new environment variables since the last snapshot?

Which resources could not be inspected with the current credentials?

Instead of “I think we have around twenty apps,” I can produce an exact inventory, along with an explicit list of the places where the inventory is incomplete.

That last part matters. Incomplete data is manageable when the gaps are visible.

Use the inventory to build the migration

The inventory should not be a dead-end discovery artifact.

I want the same data to feed the next pieces of work.

For example:

  • Dyno formations become sizing input. Process types and sizes can feed a worksheet for whatever compute platform replaces Heroku.
  • Add-ons become service decisions. Each managed service gets mapped to an equivalent on the target platform, or explicitly marked as something that needs a human decision.
  • Custom domains become the cutover inventory. Existing broken or stale domains can be found before anyone starts changing traffic.
  • Existing resources can seed infrastructure-as-code. Where appropriate, the snapshot can help generate import or configuration scaffolding so the current environment is represented before it is changed.

Run the inventory again and those downstream artifacts can be regenerated from the new state.

That is much better than maintaining three spreadsheets that immediately begin disagreeing with each other.

Discovery often makes the migration smaller

One useful result of inventory work is finding out which things do not actually need to move.

A database may already live outside Heroku.

Traffic may already pass through a proxy or CDN, making it possible to move applications independently instead of doing one large DNS cutover.

The destination environment may already contain patterns worth reusing.

Or none of those things may be true.

The point is to find out before writing the migration plan.

I do not want the target architecture to be based on assumptions about where state lives, how traffic arrives, or what already exists on the other side.

Write down what you cannot know yet

There will usually be gaps.

Maybe the available token cannot inspect part of the environment. Maybe ownership of an old app is unclear. Maybe two services appear related but nobody can yet explain the dependency.

Those are not reasons to guess.

The inventory should record what could not be collected and why. Then the gap becomes an explicit discovery item:

  • get credentials with the required scope;
  • find the owner;
  • inspect the application;
  • decide whether the resource is still needed;
  • rerun the inventory.

A known gap is work you can schedule.

An invisible gap tends to show up during the cutover.

The reusable part

Heroku is just the example.

The same approach applies whenever someone says “we need to get off this platform” but nobody has a trustworthy picture of the platform they are currently on.

Before designing the destination, make the source legible.

Build an inventory that can be regenerated. Keep it structured. Track what changed. Record what you could not see. Do not collect secrets just because an API will give them to you.

Then use that inventory as input to the migration instead of treating discovery as a document that gets written once and forgotten.

That makes the actual move a lot less exciting.

Which is generally what I want from infrastructure work.


Planning a move off Heroku, or off something else?

If you have a platform nobody can fully inventory anymore, I can help make it legible, work out what actually has to move, and build a migration plan from there.

Talk to me → Email Me Instead →