What Is Command-Based Programming?

The Big Payoff of Everything So Far

Every idea from Ch.17-24 — inheritance, interfaces, lambdas, the IO-Layer Pattern, static factories, builders — leads here. Command-based programming is WPILib's own official architecture pattern for structuring an entire robot's code. It's not the only way to write a robot program, but it's the one WPILib itself provides deep library support for, and the one most competitive FRC codebases are built around.

This chapter teaches Commands v3, the version of the framework built for WPILib 2027 (package org.wpilib.command3). Its older sibling, Commands v2, is what most existing team code — including older code of our own — still uses. You'll write v3, but you'll read plenty of v2, so Lesson 25.7 ends with a short guide to recognizing it.

Commands v3 is still in alpha testing, so a few details may change before the season; the lessons flag the parts that are changing most. On Java 17: Commands v3 doesn't run at all — it's built on a JDK feature (continuations) that Java 17 doesn't have.

Declarative, Not Imperative

Command-based is a declarative paradigm: you describe what should happen under what condition, and the library handles checking that condition every single loop, on your behalf. Compare these two ways of saying "extend the piston when a condition becomes true":

// Declarative (command-based): say what should happen, once
new Trigger(condition::get).onTrue(pneumatics.extendCommand());
// Imperative (without command-based): manually track state every single loop
if (condition.get())
{
    if (!pressed)
    {
        piston.set(FORWARD);
        pressed = true;
    }
}
else
{
    pressed = false;
}

(FORWARD is DoubleSolenoid.Value.FORWARD, brought in with a static import — Lesson 3.3.)

The declarative version is written once and never touched again; the imperative version requires you to manually manage a pressed flag correctly, forever, in every place a similar check is needed. Multiply that by dozens of buttons and sensors across a real robot, and the difference in maintainability is enormous.

Three Core Abstractions: Mechanisms, Commands, and the Scheduler

Command-based organizes an entire robot around three ideas:

  • A Mechanism represents an independently-controlled piece of hardware (or group of hardware) — a drivetrain, an intake, a climber, even an LED strip. Mechanism is an interface: your class implements Mechanism. Ch.20's IO-Layer Pattern already showed how a mechanism can hide its hardware details behind a clean interface; command-based builds directly on that.
  • A Command represents an action the robot can take. In v3, a command's whole behavior is one ordinary block of Java — it can use normal loops and if statements — that pauses once per loop so the rest of the robot keeps running (Lesson 25.2). Every command also has a name.
  • The Scheduler is the engine that runs every command, once per loop (Lesson 25.3).

Here is the extendCommand() used above, written inside the mechanism that owns the piston:

public class Pneumatics implements Mechanism
{
    private final DoubleSolenoid piston; // created in the constructor (left out here)

    public Command extendCommand()
    {
        return run(coroutine -> piston.set(FORWARD)).named("Extend Piston");
    }
}

run(...) is a default method the Mechanism interface provides (Ch.19). A command built with it automatically requires this mechanism — it claims the pneumatics while it runs. Requirements are what make resource management automatic: only one command can control a given mechanism at a time, so two different pieces of code can never fight over the same motor's output. When two commands do collide, the scheduler compares their priorities to decide which one wins (Lesson 25.3).

How It All Runs: The Scheduler

The default Scheduler — Scheduler.getDefault() — is the engine underneath all of this. Once per loop (every 20ms), your robot calls its run() method, and it checks all registered triggers for commands that should start, then runs every active command until that command pauses or finishes. Lesson 25.3 covers the scheduler's actual run sequence in depth.

Composability

Commands are recursively composable — a complex robot action (like "intake a game piece, then aim the shooter, then fire") can be built out of several small, simple commands combined together, and that composite is itself just another command, usable anywhere a command is expected. Lesson 25.2 covers this in depth.

Common Pitfalls

  • Writing large amounts of imperative logic directly in Robot.java. This works against the entire declarative philosophy command-based is built on — the goal is to describe what happens, and let the scheduler handle when.
  • Touching a mechanism's hardware from a command that doesn't require it. A command that drives a mechanism's motor without requiring that mechanism bypasses the resource-management system entirely, risking two commands fighting over the same hardware. Building commands with the mechanism's own run(...) makes the requirement automatic.
  • Assuming command-based is the only valid way to write robot code. It isn't — but it's the pattern this curriculum focuses on, since it's what WPILib itself is built to support.

Key Takeaways

  • Command-based programming is WPILib's own declarative architecture pattern: describe what should happen under what condition, once, and the library handles checking it every loop.
  • This course teaches Commands v3 (org.wpilib.command3, WPILib 2027); most existing team code still uses Commands v2, which Lesson 25.7 shows you how to read.
  • The three core abstractions are Mechanisms (hardware, an interface your classes implement), Commands (named actions), and the Scheduler (Scheduler.getDefault()) that runs them every loop.
  • A command requires the mechanisms it controls, which is what lets the scheduler guarantee only one command controls a given mechanism at a time; priorities decide who wins a conflict.
  • Commands are recursively composable — small commands combine into larger ones, which are themselves still just commands.

Derived from other-reference-repo: wpilib/command-based/01-what-is-command-based.md (WPILIB 20.1); updated for Commands v3 from allwpilib main (commandsv3/…/command3/Mechanism.java, Command.java, Scheduler.java, hatchbotcmdv3 example)