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:
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;
}
}
// 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
classPatternplus amethodPatternand/orfieldPattern, with an optionaldescriptor. Patterns use*(one segment) and**(any depth).
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):
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:
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:
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:
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:
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:
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:
| Field | What it does |
|---|---|
| chars | The character set for new names (default a–z). Swap in confusable or non-Latin glyphs to make names harder to read and tell apart. |
| deep | How long generated names are. |
| packageStrategy | How the package tree is rebuilt: ROOT, FLAT, WRAPPER (default), RANDOM, MIRROR — shown above. |
| packageDepth | Depth of the generated package structure. |
| packagePool | For 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.