Comparison Brigadier and Bukkit Commands
Registering commands
The old Bukkit way
In order to register Bukkit commands, you would define a class that extends BukkitCommand, and implements the execute(...) and tabComplete(...) methods. This might look like this:
public class BukkitPartyCommand extends BukkitCommand {
    public BukkitPartyCommand(@NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull List<String> aliases) {
        super(name, description, usageMessage, aliases);
    }
    @Override
    public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
        if (args.length == 0) {
            sender.sendMessage("Please provide a player!");
            return true;
        }
        final Player targetPlayer = Bukkit.getPlayer(args[0]);
        if (targetPlayer == null) {
            sender.sendMessage("Please provide a valid player!");
            return true;
        }
        targetPlayer.sendMessage(sender.getName() + " started partying with you!");
        sender.sendMessage("You are now partying with " + targetPlayer.getName() + "!");
        return false;
    }
    @Override
    public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
        if (args.length == 1) {
            return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
        }
        return List.of();
    }
}
After that, you can define your command like this:
this.getServer().getCommandMap().register(
    this.getName().toLowerCase(),
    new BukkitPartyCommand("bukkitparty", "Have a party", "/party <player>", List.of())
);
As you can see, you have to do a lot of manual checking in order to register a single, very simple command. But how does the Brigadier api do it?
The new Paper way
First, we need to retrieve a LiteralCommandNode<CommandSourceStack>. That's a special Brigadier class that holds some sort of command tree.
In our case, it is the root of our command. We can do that by running Commands.literal(final String literal), which returns a
LiteralArgumentBuilder<CommandSourceStack>, where we can define some arguments and executors. Once we are done, we can call
LiteralArgumentBuilder#build() to retrieve our build LiteralCommandNode, which we can then register. That sounds complicated at first,
but once you see it in action, it looks less terrifying:
public static LiteralCommandNode<CommandSourceStack> createCommand(final String commandName) {
    return Commands.literal(commandName)
        .then(Commands.argument("target", ArgumentTypes.player())
            .executes(ctx -> {
                final PlayerSelectorArgumentResolver playerSelector = ctx.getArgument("target", PlayerSelectorArgumentResolver.class);
                final Player targetPlayer = playerSelector.resolve(ctx.getSource()).getFirst();
                final CommandSender sender = ctx.getSource().getSender();
                targetPlayer.sendMessage(sender.getName() + " started partying with you!");
                sender.sendMessage("You are now partying with " + targetPlayer.getName() + "!");
                return Command.SINGLE_SUCCESS;
            }))
        .build();
}
Each .then(...) defines a new branch in our tree, which can either be a literal (Commands.literal(String)) or an argument
(Commands.argument(String, ArgumentType<T>)). Each branch may or may not define an .executes(Command) executor. This is
where all the logic happens.
We will take a closer look at that in different pages, but for now, how do we register it? Paper uses a LifecycleEventManager system. In a nutshell, that is a way to register commands (or tags) that get loaded each time the server reloads its resources, like using /reload. Registering our command looks like this:
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
    commands.registrar().register(PaperPartyCommand.createCommand("paperparty"), "Have a nice party");
});
And we are done! As you can see here, both commands do the same thing:

