r Lock MasterAccount

Renaming & layout

Names carry more meaning than any other part of a class file. getLicenceKeytells a reader everything; ad tells them nothing. Renaming strips that meaning away — from classes, methods, fields, and the package tree that holds them.

Renaming

Turn it on in techniques, and every class, method and field that you haven't explicitly kept is given a fresh, meaningless name. References are rewritten to match, so the code still links and runs — it just stops describing itself.

{ "techniques": { "rename": true } }

Before & after

Real output. A small Cart that holds a Money subtotal, run through renaming and decompiled back to Java. Watch the types, fields, methods and even theimport all collapse to two-letter names — while the wiring between them stays exactly the same:

Your code
package shop.model;
import shop.util.Money;

public class Cart {
    private Money subtotal = new Money(0L);
    private int itemCount = 0;

    public void addItem(Money money) {
        this.subtotal = this.subtotal.plus(money);
        ++this.itemCount;
    }

    public Money subtotal() {
        return this.subtotal;
    }

    public int itemCount() {
        return this.itemCount;
    }
}
After — rename
// Money  ->  aa   (its own package)
package aa;
public final class aa {
    public final /* synthetic */ long aa;
    public /* synthetic */ aa(long l) {
        this.aa = l;
    }
    public /* synthetic */ aa aa(aa aa2) {
        return new aa(this.aa + aa2.aa);
    }
    public /* synthetic */ long ab() {
        return this.aa;
    }
}

// Cart   ->  ab   (references to Money become aa.aa)
package ab;
import aa.aa;
public class ab {
    public /* synthetic */ aa ab;
    public /* synthetic */ int ac;
    public /* synthetic */ ab() {
        this.ab = new aa(0L);
        this.ac = 0;
    }
    public /* synthetic */ void ac(aa aa2) {
        this.ab = this.ab.aa(aa2);
        ++this.ac;
    }
    public /* synthetic */ aa ad() {
        return this.ab;
    }
    public /* synthetic */ int ae() {
        return this.ac;
    }
}

The /* synthetic */ notes are the decompiler pointing out that even the members' metadata has been marked to throw tools off — there's no Cart,Money, subtotal or addItem left to search for.

keep rules

Renaming breaks anything that's found by its name at runtime — reflection, class loading by string, a plugin main class the server looks up, config-mapped fields. The keep block is how you protect those: it's a list of things renaming must never touch.

{
  "keep": {
    "packages": ["com/you/api"],
    "classes":  ["com/you/MyPlugin", "com/you/EventListener"],
    "members": [
      { "classPattern": "com/you/**", "methodPattern": "on*" },
      { "classPattern": "com/you/Config", "fieldPattern": "*" }
    ]
  }
}
  • packages — keep whole package trees by internal prefix.
  • classes — keep specific classes by internal name.
  • members — fine-grained rules: a classPattern plus amethodPattern and/or fieldPattern, with an optionaldescriptor. Patterns use * (one segment) and** (any depth).
Names are internal names. Use JVM form — slashes, not dots:com/you/MyPlugin, never com.you.MyPlugin.Rule of thumb: keep your entry point, your public API packages, and anything you look up by its original name.

Package strategies

Renaming doesn't just change class names — it can rebuild the whole package tree. That reshaping is its own layer of obfuscation: it destroys the architectural map that package structure hands to a reader. Here's the same six-class jar under each packageStrategy. This is the real jar listing each time (your kept shop/App entry point stays put):

Original layout
demo/Heavy.class
demo/Report.class
demo/Sample.class
shop/App.class
shop/model/Cart.class
shop/util/Money.class

ROOT — no packages at all

Everything is hoisted into the default package. Flat, anonymous, and it makes tools that assume a package structure work harder:

packageStrategy: ROOT
aa.class
ab.class
ac.class
ad.class
ae.class
shop/App.class

FLAT — one shared package

Every class lands in a single generated package, so nothing about the grouping survives:

packageStrategy: FLAT
aa/aa.class
aa/ab.class
aa/ac.class
aa/ad.class
aa/ae.class
shop/App.class

WRAPPER — one package per class (default)

Each class gets its own single-segment package. A tidy default that keeps class-per-file tools happy while giving nothing away:

packageStrategy: WRAPPER (default)
aa/aa.class
ab/ab.class
ac/ac.class
ad/ad.class
ae/ae.class
shop/App.class

RANDOM — scattered across a pool

Classes are spread at random across a pool of generated packages (size set bypackagePool), so unrelated classes share packages and related ones don't:

packageStrategy: RANDOM
aa/aa.class
aa/ab.class
aa/ac.class
ac/ae.class
ad/ad.class
shop/App.class

MIRROR — same shape, new names

Keeps the original nesting depth but renames every segment. Notice the last two classes stay two levels deep, mirroring the shop/model and shop/util they came from — useful when something expects a particular package depth:

packageStrategy: MIRROR
aa/aa.class
ab/ab.class
ac/ac.class
ad/ae/ad.class
af/ag/ae.class
shop/App.class

rename fields

The rename block tunes how names are generated. Defaults are fine for most people:

FieldWhat it does
charsThe character set for new names (default a–z). Swap in confusable or non-Latin glyphs to make names harder to read and tell apart.
deepHow long generated names are.
packageStrategyHow the package tree is rebuilt: ROOT, FLAT, WRAPPER (default), RANDOM, MIRROR — shown above.
packageDepthDepth of the generated package structure.
packagePoolFor RANDOM: how many packages to spread classes across.

Renaming is deterministic when you set a seed, so the same class keeps the same obfuscated name across releases — important for a public API that other code compiles against.