Ortus Coding Standards
Overview
Ortus Solutions maintains consistent formatting rules across all BoxLang, CFML, and Java
source code. These rules are enforced via the OrtusJava Eclipse formatter profile
(ortus-java-style.xml) for Java, and via the conventions documented here for BoxLang
and CFML. When in doubt, match what already exists in the file you are editing.
Indentation
Use tabs for indentation — never spaces for the leading indent.
- Class body: indented one tab from the class declaration.
- Method body: indented one tab from the method signature.
- Switch cases: indented one tab from
switch. - Case body: indented one tab from the
caselabel. - Continuation lines (wrapped expressions): indented one level.
// Java — tab-indented, end-of-line braces
public class UserService {
public User findById( int id ) {
if ( id <= 0 ) {
throw new IllegalArgumentException( "id must be positive" );
}
return repository.find( id );
}
}
// BoxLang — same indentation rules
class UserService {
function findById( required numeric id ) {
if ( id <= 0 ) {
throw( type = "InvalidArgument", message = "id must be positive" )
}
return variables.repository.find( arguments.id )
}
}
Spacing Inside Parentheses
Always place a space after ( and before ) in:
- Method declarations
- Method invocations
- Control flow keywords (
if,for,while,switch,catch,synchronized) - Casts
- Parenthesized expressions
// Java
public void process( String name, int count ) {
if ( count > 0 ) {
doThing( name );
}
var result = ( String ) rawValue;
}
// BoxLang
function process( required string name, required numeric count ) {
if ( count > 0 ) {
doThing( name )
}
}
Never place a space between a function name/keyword and its opening (:
// WRONG
doThing ( name );
// RIGHT
doThing( name );
No space inside empty parentheses:
// RIGHT
init()
list()
Brace Style
Use K&R / end-of-line brace placement — opening brace on the same line as the statement, not on its own line.
// RIGHT
public void save( User user ) {
if ( user.isValid() ) {
repository.save( user );
} else {
throw new ValidationException( "invalid user" );
}
}
// WRONG — Allman style, do not use
public void save( User user )
{
...
}
The same rule applies to:
- Class declarations
- Method declarations
if/else/else iffor/while/do-whiletry/catch/finallyswitch- Array initializers
Always Use Braces
Even for single-statement bodies, always use braces. Never place an if or loop
body on the same line as its condition.
// WRONG — no braces, body on same line
if ( user == null ) return;
for ( var item : items ) process( item );
// RIGHT
if ( user == null ) {
return;
}
for ( var item : items ) {
process( item );
}
Spacing Around Operators
Place one space on each side of:
- Assignment operators (
=,+=,-=, etc.) - Comparison operators (
==,!=,<,>,<=,>=) - Arithmetic operators (
+,-,*,/,%) - Bitwise operators (
&,|,^,~) - Shift operators (
<<,>>,>>>) - String concatenation (
&in BoxLang/CFML,+in Java) - Lambda arrows (
->in Java,->/=>in BoxLang) - Ternary operator (
?and:)
// Java
var total = price * quantity;
var result = isValid ? "yes" : "no";
var upper = items.stream().map( s -> s.toUpperCase() ).collect( Collectors.toList() );
// BoxLang
var total = price * quantity
var result = isValid ? "yes" : "no"
var upper = items.map( ( s ) -> uCase( s ) )
No space for:
- Unary operators before their operand:
!condition,++i,-value - After
!:!isValid(no space between!andisValid) - Ellipsis:
String... values(no space before...)
Column Alignment
When multiple related declarations or assignments appear in a block, align on columns so they read like a table. This applies to:
- Field (property) declarations
- Local variable declarations
- Assignment groups
// Java — aligned field declarations
private final UserRepository userRepository;
private final EmailService emailService;
private final int maxRetries;
// BoxLang — aligned assignments
var firstName = "Alice"
var lastName = "Smith"
var email = "alice@example.com"
var active = true
this.name = "My App"
this.sessionManagement = true
this.sessionTimeout = createTimespan( 0, 1, 0, 0 )
this.timezone = "UTC"
Apply alignment within logical groups (separated by blank lines). Do not force alignment across unrelated declarations.
Blank Lines
| Context | Rule |
|---|---|
| Between methods | 1 blank line |
| Between abstract methods | 1 blank line |
| Between fields | 0 blank lines (fields are grouped together) |
After package declaration |
1 blank line |
| First declaration inside a class body | 1 blank line |
| Beginning of a method body | 0 blank lines |
| End of a code block | 0 blank lines |
Before a code block (e.g., before {) |
0 blank lines |
// Java — correct blank line usage
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
public OrderService( OrderRepository orderRepository, PaymentService paymentService ) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
}
public Order create( CreateOrderRequest request ) {
var order = buildOrder( request );
orderRepository.save( order );
return order;
}
private Order buildOrder( CreateOrderRequest request ) {
return new Order( request.getItems(), request.getUserId() );
}
}
Naming Conventions
| Item | Convention | Example |
|---|---|---|
| Java classes | PascalCase |
UserService, OrderProcessor |
| Java interfaces | PascalCase |
UserRepository, Serializable |
| Java methods | camelCase |
getUserById(), processOrder() |
| Java fields | camelCase |
userRepository, maxRetries |
| Java constants | UPPER_SNAKE_CASE |
MAX_RETRIES, DEFAULT_TIMEOUT |
| Java packages | lowercase.dot.separated |
ortus.boxlang.runtime.services |
| BoxLang classes | PascalCase |
UserService.bx, OrderProcessor.bx |
| BoxLang functions | camelCase |
getUserById(), processOrder() |
| BoxLang variables | camelCase |
userProfile, orderTotal |
| BoxLang templates | camelCase or kebab-case |
userProfile.bxm, order-detail.bxm |
| BoxLang scripts | camelCase |
buildReport.bxs |
| BoxLang constants (local) | UPPER_SNAKE_CASE |
var MAX_RETRIES = 3 |
Comments and Javadoc
Javadoc (Java)
Write Javadoc for all public classes and public/protected methods.
/**
* Finds a user by their unique identifier.
*
* @param id the user's primary key (must be positive)
*
* @return the matching {@link User}, or {@code null} if not found
*
* @throws IllegalArgumentException if {@code id} is not positive
*/
public User findById( int id ) {
...
}
Rules:
- Opening
/**and closing*/on their own lines. - One space after
*on every body line. @param,@return, and@throwstags on separate lines.- Do not join description lines — keep each sentence on its own line.
@paramdescriptions aligned (not required but preferred for readability).
Inline Comments (Java)
// Use single-line comments for brief explanations above the line they describe.
// Never end-of-line comments for non-obvious things — put them above instead.
// Calculate total including tax
var total = basePrice * ( 1 + taxRate );
Do not leave commented-out code in committed files.
BoxLang Documentation Comments
/**
* Finds a user by their unique identifier.
*
* @id The user's primary key. Must be a positive integer.
* @return struct containing user data, or an empty struct if not found.
*/
struct function findById( required numeric id ) {
...
}
BoxLang-Specific Conventions
Semicolons
Do not use semicolons on regular statements in BoxLang. They are optional and the standard is to omit them for cleaner code.
// RIGHT — no semicolons
var result = calculate( x, y )
return result
// WRONG
var result = calculate( x, y );
return result;
Exceptions where semicolons ARE required:
- Property declarations:
property name="fieldName" type="string"; - Multiple statements on a single line (avoid this pattern)
Lambdas vs Closures
Use lambdas (->) when the function is deterministic and operates only on
its own arguments:
// Lambda — only uses the item argument (no outer scope access)
var doubled = numbers.map( ( n ) -> n * 2 )
var upper = words.map( ( w ) -> w.uCase() )
Use closures (=>) when the function accesses variables from the enclosing
scope or calls external BIFs:
// Closure — accesses outer variable `threshold`
var filtered = numbers.filter( ( n ) => n > threshold )
// Closure — calls external BIF
var encoded = values.map( ( v ) => encodeForHTML( v ) )
Argument Scope in Closures
Inside a closure, do not use the arguments scope to access outer function
parameters. The arguments scope inside the closure refers only to the
closure's own parameters:
// WRONG — arguments.name refers to the closure's arguments, not the outer function's
function process( required string name ) {
var found = items.filter( ( item ) => item.name != arguments.name )
}
// RIGHT — use unscoped name; BoxLang finds it in the enclosing scope
function process( required string name ) {
var found = items.filter( ( item ) => item.name != name )
}
Function Call Spacing
// RIGHT — spaces inside parentheses, space after comma
doThing( arg1, arg2 )
createUser( firstName = "Alice", active = true )
// WRONG — no spaces inside parens
doThing(arg1, arg2)
Struct and Array Literals
// Preferred — literal syntax with alignment
var config = {
host : "localhost",
port : 5432,
database: "myapp"
}
var roles = [ "admin", "editor", "viewer" ]
Multi-Line Method Calls
When a call does not fit on one line, wrap arguments with one argument per line and align them:
// Java
var result = userService.createUser(
request.getFirstName(),
request.getLastName(),
request.getEmail(),
request.getRoleId()
);
// BoxLang
var result = userService.createUser(
firstName = request.firstName,
lastName = request.lastName,
email = request.email,
roleId = request.roleId
)
Wrap before operators, not after:
// RIGHT — operator at start of wrapped line
var isEligible = user.isActive()
&& user.hasVerifiedEmail()
&& !user.isSuspended();
Java-Specific Patterns
Import Order
Group imports in this order with one blank line between groups:
java.*andjavax.*- Third-party libraries
ortus.boxlang.*internal classes
Do not use wildcard imports (import java.util.*).
Null Checks
Prefer Objects.requireNonNull in constructors and public entry points:
public UserService( UserRepository repo ) {
this.repo = Objects.requireNonNull( repo, "repo must not be null" );
}
final Fields
Declare all injected dependencies and constructor-set fields as final:
private final UserRepository userRepository;
private final EmailService emailService;
Avoid Magic Numbers
Extract literal numbers and strings into named constants:
// WRONG
if ( retries > 3 ) { ... }
// RIGHT
private static final int MAX_RETRIES = 3;
if ( retries > MAX_RETRIES ) { ... }
Common Anti-Patterns to Avoid
| Anti-Pattern | Fix |
|---|---|
| Allman-style braces | Use K&R (end-of-line) braces |
No spaces inside ( ) in calls |
Always doThing( arg ) not doThing(arg) |
| Semicolons on BoxLang statements | Remove unless a property declaration |
| Unaligned field groups | Align with spaces/tabs to column |
arguments.name inside closure accessing outer scope |
Remove scope prefix |
| Comment-out dead code | Delete it; use version control |
| Wildcard imports in Java | Use explicit imports |
| Magic numbers | Extract to named constants |
Mutable public fields in Java |
Use private final + accessor |
One-liner if without braces |
Always use braces |