Menu

Guidefor modders3 min read

Porting a mod to a new Minecraft version

Mappings, the compile errors that come first, the behaviour changes that come later, and the order that makes a port take days instead of weeks.

Why a port is work at all

Minecraft ships obfuscated. Class and method names in the released jar are meaningless short strings, and they change every build, because nothing is meant to depend on them.

Mappings map those back to readable names, and a mapping set is specific to one Minecraft version. So your mod is not written against a stable library, it is written against a snapshot of the game's internals as they happened to be named at one moment.

That is why a port is never just a version bump. Names moved, methods changed shape, and some things are simply gone.

Do it in this order

The mistake that makes ports drag on is fixing errors as they appear in the file you happen to have open. Work in layers instead.

1. Update the toolchain, before touching your code

Bump the Minecraft version, the mappings, the loader version and the API version in your build file. Nothing else. Then compile and look at the wall of errors.

That wall is your actual work list. Reading it before changing anything tells you whether this is a two-hour port or a two-week one, which is worth knowing before you start.

2. Fix the renames

Most of the errors will be one thing renamed. These are mechanical: find the new name, apply it everywhere.

Two things make this much faster:

  • A mapping diff between the two versions, which tells you what became what, rather than guessing from similar names.
  • Fixing all instances of one rename at once rather than file by file. The same method usually appears in a dozen places.

3. Fix the structural changes

What is left is real work: a method that changed signature, a class that split, a system that was replaced.

Here the loader's migration notes are worth reading properly, and so is the decompiled source. When a method is gone with no obvious replacement, search the decompiled game for its old behaviour and see what performs it now.

4. Fix the registration order

Both loaders move registration timings between versions more often than they change the registration API. Code that registered at the right moment can now be too early or too late.

These fail at runtime rather than at compile time and often produce confusing errors about something being absent, so they are worth checking deliberately rather than waiting to trip over them.

5. Then test behaviour

Everything compiles and loads. Now find the things that changed quietly: an event firing at a different point, a value that can now be null, an ordering that flipped.

Nothing errors here. Your mod is just wrong. This is the layer that produces bug reports a week after release, and the only defence is exercising your mod's actual features rather than confirming the game starts.

Start at the pre-release

The release cycle gives you a schedule if you use it:

  • Snapshots. Things are still moving. Porting now means redoing it.
  • Pre-releases. Feature complete, API mostly settled. This is when to start.
  • Release candidates. If your port works here, it works on release day, because the release is usually this exact code.

Being ready on release day is worth more to your users than any feature you could have added that month, particularly for library mods, where everything downstream is waiting on you.

Supporting two versions at once

Two shapes, and picking wrong costs months:

Shared module plus per-version sources. One project, common code in one place, small version-specific source sets. Right when the differences are small.

Separate branches. Right when they are not. Once the versions diverge enough, keeping one codebase that satisfies both costs more than maintaining two, and produces code neither of you enjoys reading.

Be honest about which you are in. A shared module full of version checks is a branch that has not admitted it yet.

Publishing the port

  • Declare the exact Minecraft versions in your release metadata, not just in the description. That metadata is what catalogues read, including the mod catalogue here, which filters on those exact declarations. Writing "1.21" when you mean every 1.21.x, or the reverse, is what makes users and catalogues disagree.
  • Declare your loader. A build for one never runs on another. See why mods are loader specific.
  • Bump your dependency ranges to what you actually tested against.
  • Write a real changelog if behaviour changed. A port that silently alters a mechanic people built around is worse than one that says so.

Frequently asked

Why does the game's code have different names every version?

Mojang ships obfuscated builds, so internal names are meaningless strings that change every build. Mappings map them back to readable names, and the mapping set is version specific.

Can I support two Minecraft versions from one branch?

For small differences, yes, with a shared module and per-version source sets. Across a large change the branches diverge enough that maintaining both costs more than maintaining two.

When should I start porting?

At the pre-release stage. The API is settled enough by then to be worth your time, and starting at the snapshot stage means redoing work as things move.

How do I find what replaced a removed method?

The mapping diff between versions, and the loader's own migration notes. Failing both, search the decompiled source for the old behaviour and see what calls it now.

Referenced on this site

Last reviewed 2026-07-27. Version data on this site updates automatically; this guide is reviewed by hand when the ecosystem changes.