sign in
Start typing to search across all 16 weeks…
home/style guide
reference

WRT Java Style Guide

how we write code on team 2974. read this before writing a single line for a real robot :)

this is not optional!! PRs that don't follow these conventions will get review comments and sent back. it's not personal, it's just how we keep 8+ programmers from losing their minds during build season :D

naming conventions

we use specific prefixes so you can tell what kind of thing a variable is just by looking at its name. no guessing required.

prefixwhat it's forexample
kconstants (final static values, usually in a Constants.java nested class)kShooterMotorID, kMaxTranslationSpeed_mps
m_member/instance variables (fields on a class)m_drivetrain, m_shooter, m_isRunning
trg_Trigger objects (WPILib button bindings)trg_shoot, trg_intakeIn
log_logging/telemetry objects (AdvantageKit, DataLog)log_robotPose, log_shooterRPM
sig_Phoenix 6 StatusSignal objectssig_velocity, sig_appliedVolts
java — naming in practice
public class ShooterSubsystem extends SubsystemBase {

    // m_ prefix for instance fields
    private final TalonFX m_shooterMotor;
    private double m_targetRPS = 0.0;
    private boolean m_isSpunUp = false;

    public ShooterSubsystem() {
        // k prefix for constants from Constants.java
        m_shooterMotor = new TalonFX(ShooterK.kMotorID);
    }
}

constants file structure

all constants live in Constants.java using nested static classes with the K suffix (short for "constants" -- yes it's a convention, just roll with it lol).

java — Constants.java structure
public final class Constants {

    // one nested class per subsystem
    public static final class ShooterK {
        public static final int    kMotorID    = 11;
        public static final double kIdleRPS    = 0.0;
        public static final double kShootRPS   = 80.0;
        public static final String kLogTab     = "Shooter";
    }

    public static final class DriveK {
        // include units in the name to avoid conversion bugs!!
        public static final double kMaxTranslation_mps = 4.5;
        public static final double kMaxRotation_radps  = 9.42;
        public static final double kWheelRadius_m      = 0.0508;
    }

    public static final class ControllerK {
        public static final int kDriverPort   = 0;
        public static final int kOperatorPort = 1;
    }
}

unit suffixes!! always include the unit in the constant name for any physical value. kShootSpeed is ambiguous. kShootRPS (rotations per second) or kMaxTranslation_mps (meters per second) is not. this has literally prevented robot crashes. not joking.

casing rules

camelCase
variables + methods
motorSpeed, isRunning, getTargetAngle(). first word lowercase, each new word capitalized.
PascalCase
classes + interfaces
ShooterSubsystem, DriveCommand, RobotContainer. every word capitalized.
k + PascalCase
constants
kMotorID, kMaxSpeed_mps. always k lowercase, then PascalCase. not SCREAMING_SNAKE on this team.
m_ + camelCase
member variables
m_shooter, m_isSpunUp. the underscore is part of the prefix, not the name.

we don't use SCREAMING_SNAKE_CASE for constants on this team. i know java tutorials use it. we don't. use k prefix + PascalCase. yes this is different from standard java conventions. no we don't care lol

comments

comments explain why, not what. the code already says what it does. you explain why it's doing that specific thing, especially if it looks weird.

java — bad vs good comments
// BAD: just restates the code, adds nothing
// set motor speed to 0.5
m_motor.set(0.5);

// GOOD: explains why 0.5, what this achieves
// ramp slowly to avoid current spike on enable
m_motor.set(0.5);

// ALSO GOOD: explains a non-obvious workaround
// inverted because motor is mounted backwards on Oasis (but not Watergate)
m_motor.setInverted(true);

javadoc for public methods

any public method that's non-trivial needs a javadoc. especially anything other subsystems or commands will call.

java — javadoc format
/**
 * Sets the shooter target speed and begins spinning up.
 *
 * @param targetRPS desired flywheel speed in rotations per second
 */
public void setTargetRPS(double targetRPS) {
    m_targetRPS = targetRPS;
    m_controller.setSetpoint(targetRPS);
}

magic numbers = banned :)

a magic number is a raw number in your code with no explanation. they're basically bugs waiting to happen because nobody knows what they mean 6 months later.

java — magic numbers
// BAD -- what is 11? what is 80? who knows!!
m_motor = new TalonFX(11);
m_controller.setSetpoint(80.0);

// GOOD -- self-documenting, easy to change from one place
m_motor = new TalonFX(ShooterK.kMotorID);
m_controller.setSetpoint(ShooterK.kShootRPS);

subsystem structure

all subsystems extend SubsystemBase and follow the same section layout. makes it easier to jump between files without getting lost.

java — subsystem template
package frc.robot.subsystems;

import edu.wpi.first.wpilibj2.command.SubsystemBase;
import com.ctre.phoenix6.hardware.TalonFXS;
import com.ctre.phoenix6.configs.TalonFXSConfiguration;
import com.ctre.phoenix6.configs.CurrentLimitsConfigs;
import com.ctre.phoenix6.configs.MotorOutputConfigs;
import com.ctre.phoenix6.signals.NeutralModeValue;
import com.ctre.phoenix6.signals.InvertedValue;

public class ExampleSubsystem extends SubsystemBase {

    // ---- HARDWARE -----------------------------------------------
    private final TalonFXS m_motor;

    // ---- STATE --------------------------------------------------
    private double m_targetRPS = 0.0;

    // ---- CONSTRUCTOR --------------------------------------------
    public ExampleSubsystem() {
        m_motor = new TalonFXS(ExampleK.kMotorID);
        configureMotor();
    }

    private void configureMotor() {
        var currentCfg = new CurrentLimitsConfigs()
            .withStatorCurrentLimit(ExampleK.kCurrentLimit_A)
            .withSupplyCurrentLimit(15)
            .withStatorCurrentLimitEnable(true)
            .withSupplyCurrentLimitEnable(true);

        var outputCfg = new MotorOutputConfigs()
            .withInverted(InvertedValue.CounterClockwise_Positive) // flip if needed
            .withNeutralMode(NeutralModeValue.Coast);

        m_motor.getConfigurator().apply(
            new TalonFXSConfiguration()
                .withCurrentLimits(currentCfg)
                .withMotorOutput(outputCfg)
        );
    }

    // ---- PUBLIC API ---------------------------------------------
    public void setSpeed(double rps) { m_targetRPS = rps; }

    public boolean isAtTarget() {
        return Math.abs(m_motor.getVelocity().getValueAsDouble() - m_targetRPS) < ExampleK.kTolerance_rps;
    }

    // ---- PERIODIC -----------------------------------------------
    @Override
    public void periodic() {
        // runs every 20ms. telemetry + closed-loop control goes here
        m_motor.set(m_targetRPS / ExampleK.kMaxRPS);
    }
}

no while loops in robot code

seriously, no while loops. the robot runs a 20ms control loop managed by WPILib. a blocking while loop freezes that loop and the watchdog kills the robot. use commands, state machines, or periodic methods instead.

java — while loop vs command-based
// NEVER DO THIS in robot code
while (!m_shooter.isAtTarget()) {
    // blocks the entire robot loop. watchdog WILL fire. robot dies.
}

git conventions

check O1 for the full git workflow. these are the non-negotiables:

rulewhy
never commit directly to mainmain = always working. one bad push during competition is a crisis
branch name: type/short-descfeature/shooter-pid, fix/intake-stall, refactor/constants-cleanup
keep PRs small and focusedone feature per PR. giant PRs are painful to review and silently break things
needs at least one approvalone of our mentors (Steve or Banks) reviews before merging to main

WPILib 2026 notes

WPILib updates every year and things get deprecated. here's what matters for 2026:

Java 17
still required
WPILib 2026 still uses Java 17. WPILib installer sets this up automatically. don't mess with the JDK path.
CTRE / Krakens
everything is TalonFX
we run Kraken X60s (TalonFX) across the whole robot -- drive, shooter, intake, all of it. all config goes through Phoenix 6 configurator API. no REV motors on this team.
Phoenix 6
still current — use it
Phoenix 6 is still the CTRE library. TalonFX + Phoenix 6 is what we use on the drivetrain. if you see Phoenix 5 in old code, do not copy it — it's dead.
Commands
stable — no big changes
command-based architecture hasn't changed. SubsystemBase, Scheduler, Triggers all work the same. Choreo is still our path following library.

not sure what version we're on? check build.gradle in the robot repo. look for the wpilib version string. if you're confused just ask before changing anything :)

heads up — System Core is coming (2027)

for the 2027 season we're switching to System Core, FIRST's new robot controller that replaces the roboRIO. it runs on a faster processor, has more I/O, and uses a new HAL underneath — but WPILib abstracts most of the differences so the code you write now will largely carry over. a few things to know now so you're not caught off guard:

same WPILib API
your code still works
SubsystemBase, Commands, Triggers, TalonFX — all the same. WPILib wraps the hardware. you won't be rewriting the whole robot.
new HAL
low-level stuff changes
direct register access or anything that talks to roboRIO hardware specifically (custom DIO tricks, etc.) will need updating. most teams won't hit this.
vendor libs
watch for updates
Phoenix 6 / CTRE will need a 2027-compatible release. don't upgrade vendor deps until that drops — check team Discord before touching build.gradle.
deployment
different image process
System Core has a different imaging/flashing process than roboRIO. there will be a team doc on this before build season. don't wing it at comp when you're helping other teams

don't stress about this now. other teams are still on roboRIO for 2026, while we will be on systemcore because ~~we're just that goated~~ we're a beta testing team. learn the current stack well first — the concepts transfer directly. System Core is just new hardware underneath the same WPILib you already know.

pre-PR checklist

run through this before opening a pull request. if you can't check all of these, fix it first!!

naming
prefixes used correctly?
k, m_, trg_, log_ where appropriate. no raw magic numbers. no SCREAMING_SNAKE.
comments
public methods documented?
any public non-trivial method has a javadoc. workarounds have inline comments explaining why.
constants
constants in Constants.java?
no hardcoded IDs, speeds, or PID values in subsystem files. all in the relevant K class.
loops
no while loops?
no blocking loops anywhere in robot code. check commands, periodic methods, and auto routines.
applyConfig
TalonFX configured?
all TalonFX motors need a TalonFXConfiguration applied via getConfigurator().apply() in the constructor. neutral mode, current limits, inversions -- all in there.
sim
doesn't crash in sim?
run in simulation mode. it doesn't need to work perfectly, it should just do what you intended it to do.