Migrations are a way to fixup a save file which was used in an older version of the game or mod. Migrations are any .lua file or any .json file stored in the mods "migrations" folder.

Migrations are sorted by mod order first and then by the migration file name using lexicographical comparison. Each save file remembers which migrations from which mods have been applied and will not apply the same migration twice. When adding a mod to an existing save all migration scripts for that mod will be run. Migrations are typically used to but are not limited to: changing prototype types or correct research/recipe states after changes.

JSON Prototype migrations allow changing one prototype to another prototype. Typically this is done when re-naming something.

stone-wall was previously named "wall" and was re-named:
    ["wall", "stone-wall"]
    ["wall", "stone-wall"]

Multiple such migrations can be applied at once. Note: not all prototype types support migrations. As of 0.14 the following types are supported: entity, equipment, item, recipe, tile, technology. JSON migrations are applied as a map is loaded.

Lua migrations allow altering the loaded game state. Typically this is done when recipes or technologies have changed. The game resets recipes/ technologies any time mods, prototypes, or startup settings change, so this does not need to be done in migration scripts.

When the rail-chain-signal and additional tank ammo were added to the game, previous existing technology was changed so it would unlock the recipes. The game however could have already researched those technologies leaving the recipes disabled.
for index, force in pairs(game.forces) do
  local technologies = force.technologies
  local recipes = force.recipes

  recipes["rail-chain-signal"].enabled = technologies["rail-signals"].researched

  if technologies["tanks"].researched then
    recipes["explosive-cannon-shell"].enabled = true