agentsclimarketplace

Best practices

Skill ortus-boxlang/skills/boxlang-developer/best-practices

BoxLang AI skills repository and Claude Plugin

Install
npx -y skills add ortus-boxlang/skills --skill best-practices

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use this skill when writing, reviewing, or improving BoxLang code to ensure it follows community best practices for naming, structure, scoping, error handling, performance, and maintainability.

SKILL.md

11.9 KB, as published. Nobody here has run it

BoxLang Best Practices

Overview

BoxLang is a modern dynamic JVM language. These best practices reflect idiomatic BoxLang patterns informed by the language's design, CFML heritage, and JVM performance characteristics. Following them produces code that is readable, maintainable, performant, and safe.


Naming Conventions

ItemConventionExample
VariablescamelCaseuserProfile, orderTotal
Functions/MethodscamelCasegetUserById(), processOrder()
ClassesPascalCaseUserService, OrderProcessor
ConstantsUPPER_SNAKE_CASEMAX_RETRIES, DEFAULT_TIMEOUT
Files (classes)PascalCaseUserService.bx
Files (templates)camelCase or kebab-caseuserProfile.bxm, order-details.bxm
Files (scripts)camelCasebuildReport.bxs
// Good
class UserService {
    function getUserById( required numeric id ) {
        var MAX_RETRIES = 3
        var userId = arguments.id
        return userRepository.find( userId )
    }
}

Variable Scoping

Always declare local variables with var inside functions to avoid polluting the variables scope (the component-level scope).

// BAD — leaks into variables scope
function process() {
    result = doWork()
    return result
}

// GOOD — properly local
function process() {
    var result = doWork()
    return result
}

Use explicit scope prefixes when ambiguity exists:

function getUser( required numeric id ) {
    // Explicitly scope to avoid confusion
    var userData = variables.userRepo.find( arguments.id )
    return userData
}

Avoid Shadowing Built-In Scope Names

Never declare local variables with the same name as a built-in scope. BoxLang resolves unscoped variable references by walking the scope chain, and shadowing a scope name breaks access to that scope's data and causes confusing bugs.

Reserved scope names to avoid as variable names: session, server, request, url, form, application, cgi, thread

// BAD — shadows the `session` scope, breaking all session access
function login( required string user ) {
    var session = { user: arguments.user }  // DON'T do this
    session.user = arguments.user           // writes to local var, not session scope
}

// BAD — shadows the `url` scope
var url = parseUrl( input )                 // url.params is now broken

// GOOD — use descriptive names that don't collide
function login( required string user ) {
    var sessionData = { user: arguments.user }
    session.user = arguments.user           // correctly writes to session scope
}

// GOOD — use a different name
var urlParts = parseUrl( input )
var queryParams = url.params                // url scope still accessible

When you need a local reference to scope data, use a name that describes the data, not the scope itself:

Instead ofUse
var session = ...var sessionData, var userSession, var sess
var request = ...var requestData, var req, var httpRequest
var url = ...var urlParts, var currentUrl, var uri
var form = ...var formData, var formFields, var payload
var application = ...var appConfig, var appData, var settings
var cgi = ...var cgiData, var serverInfo
var thread = ...var threadInfo, var workerThread
var server = ...var serverInfo, var hostConfig

Scope Lookup Performance

BoxLang walks the scope chain on each unscoped variable access. In hot code paths (tight loops, high-traffic request handlers), scope your variables explicitly for predictable performance:

// GOOD in hot paths — no scope chain walk
var len = arguments.items.len()
for ( var i = 1; i <= len; i++ ) {
    // process arguments.items[ i ]
}

Functions

Always Declare Argument Types and Required Status

// BAD — no type information
function processOrder( order, userId ) { ... }

// GOOD — self-documenting, validated at runtime
function processOrder( required struct order, required numeric userId ) {
    ...
}

Use Named Arguments for Clarity

// Hard to read
createUser( "John", "Doe", "[email protected]", true )

// GOOD — named arguments document intent
createUser(
    firstName = "John",
    lastName  = "Doe",
    email     = "[email protected]",
    active    = true
)

Return Types

Declare return types for public functions to document contracts and enable better IDE support:

struct function getUser( required numeric id ) {
    return userService.find( arguments.id )
}

array function listActiveUsers() {
    return userService.findByStatus( "active" )
}

Error Handling

Catch Specific Exception Types

// BAD — catches everything, hides bugs
try {
    processOrder( order )
} catch ( any e ) {
    logError( e )
}

// GOOD — handle specific cases, re-throw unknown
try {
    processOrder( order )
} catch ( "Database" e ) {
    handleDatabaseError( e )
} catch ( "Validation" e ) {
    return { success: false, message: e.message }
} catch ( any e ) {
    // Re-throw unexpected errors
    rethrow
}

Use cffinally for Cleanup

transaction {
    try {
        updateOrder( order )
        chargePayment( payment )
        transactionCommit()
    } catch ( any e ) {
        transactionRollback()
        rethrow
    }
}

Null Safety

Use the safe-navigation operator (?.) and Elvis operator (?:) to avoid null pointer errors:

// Null-safe chained access
var city = user?.address?.city ?: "Unknown"

// Null-safe method calls
var count = order?.items?.len() ?: 0

Prefer isNull() over direct comparisons with null:

if ( isNull( result ) ) {
    return getDefault()
}

Closures vs Lambdas

Use lambdas (->) for pure deterministic operations on their arguments only. Use closures (=>) when accessing outer scope variables or calling external functions/BIFs.

// Lambda — only uses the item argument (pure transform)
var doubled = numbers.map( ( n ) -> n * 2 )

// Closure — accesses outer variable `threshold`
var filtered = numbers.filter( ( n ) => n > threshold )

// Closure — calls external BIF
var upper = words.map( ( w ) => uCase( w ) )

Struct and Array Literals

Prefer literal syntax over constructor functions:

// GOOD — literal syntax
var user = {
    name:  "Alice",
    email: "[email protected]",
    roles: [ "admin", "user" ]
}

// Avoid unless dynamic keys are needed
var user = structNew()
user.name = "Alice"

Use ordered struct literal syntax when key order matters:

// Ordered struct (insertion order preserved)
var config = [=
    host: "localhost",
    port: 5432,
    database: "myapp"
=]

String Interpolation

Use #expression# for interpolation in strings and templates. For complex expressions, assign to a variable first for readability:

// Simple interpolation
var message = "Hello, #user.name#!"

// Complex — extract first
var formattedDate = dateTimeFormat( now(), "long" )
var header = "Report generated on #formattedDate#"

Component (Class) Design

Constructor Pattern

Use init() as the constructor. Return this for fluent construction:

class UserService {

    property name="userRepo" inject="UserRepository"

    function init( required UserRepository userRepository ) {
        variables.userRepo = arguments.userRepository
        return this
    }

}

Keep Classes Focused (Single Responsibility)

Each .bx class should have one primary purpose. Avoid "god objects" that handle unrelated concerns. Split into service, repository, and model layers.


Performance Tips

  1. Cache expensive lookups — store results in application scope for shared read-only data; invalidate on change.
  2. Use trustedCache=true in production — prevents disk I/O on class file checks.
  3. Pre-compute in constructors — if a value won't change, compute it once during instantiation.
  4. Prefer each() / map() / filter() over manual loops for collection work — more readable and JIT-friendly.
  5. Use virtual threads (runAsync defaults) for I/O-bound async work; use fixed-pool executors for CPU-bound work.

Code Organization

/app
  /models           -- Business domain classes (.bx)
  /services         -- Service layer classes (.bx)
  /repositories     -- Data access classes (.bx)
  /handlers         -- Request handlers / controllers (.bx)
  /views            -- Templates (.bxm)
  /includes         -- Reusable partial templates (.bxm)
  /scripts          -- Standalone scripts (.bxs)
  Application.bx    -- Application lifecycle

Common Anti-Patterns to Avoid

Anti-PatternProblemFix
Unscoped vars in functionsVariables bleed into component scopeAlways use var
Shadowing scope names (session, url, form, etc.)Breaks access to built-in scopesUse descriptive names like sessionData, formData
Silent catch-all catch(any)Swallows unexpected errorsRe-throw unknown exceptions
Logic in templatesHard to test, poor separationMove to services/handlers
Direct SQL in handlersNo reuse, SQL injection riskUse repository classes with parameterized queries
Storing secrets in codeSecurity riskUse environment variables via ${env.VAR_NAME} in config
Overusing application scopeConcurrency bugsUse proper locking (bx:lock) for writes
arr[ 0 ] (zero-based index)ArrayIndexOutOfBoundsExceptionBoxLang arrays are 1-indexed: use arr[ 1 ] or arr.first()
Trailing ; on statementsNoisy / inconsistent styleSemicolons are optional on statements — omit them
cfheader() / <cfabort>CFML syntax, not BoxLangUse bx:header, bx:abort, etc.
Named args on Java objectsNot supported, throws runtime errorAlways use positional arguments for Java method calls
createObject("java","path") per callVerbose, repeated boilerplateimport java:fully.qualified.Class once, then use the class name directly

BoxLang vs CFML Quick-Reference

BoxLang evolved from CFML but uses different syntax for many constructs. Do NOT use cf-prefixed tags or functions in BoxLang source files.

CFMLBoxLang Equivalent
cfheader(name="X", value="Y")bx:header name="X" value="Y";
cflocation(url="...")bx:location url="...";
cfabortbx:abort;
cfparam name="x" default=""bx:param name="x" default="";
cfinclude template="f.cfm"bx:include template="f.bxm";
<cfsilent>bx:silent { ... }
createObject("java","path.Class")import java:path.Class then new java:path.Class()

Array Best Practices

BoxLang arrays are 1-indexed. This is a common source of bugs for developers coming from Java/JavaScript backgrounds.

var items = [ "a", "b", "c" ]

// CORRECT
var first = items[ 1 ]          // "a"
var last  = items[ items.len() ] // "c"
var first = items.first()        // preferred — more readable
var last  = items.last()         // preferred

// WRONG (throws ArrayIndexOutOfBoundsException)
var first = items[ 0 ]

// Looping — i starts at 1
for ( var i = 1; i <= items.len(); i++ ) {
    process( items[ i ] )
}

Passing Single Values to Java Varargs

Java varargs methods require a BoxLang array, not a bare scalar value:

// CORRECT — wrap in array
storage.query( "SELECT * FROM t WHERE id = ?", [ requestId ] )

// WRONG — bare value is not accepted by Java varargs
storage.query( "SELECT * FROM t WHERE id = ?", requestId )    // throws

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.