Zero-Dependency Command-Line Applications with Modern Java
Overview
Java CLI App applies the BCE pattern to multi-file command-line applications built with Java 25.
Applications are packaged as executable JARs using zb (Zero Dependencies Builder), requiring no
Maven or Gradle. The approach favors unnamed classes with top-level methods, records, enums, and
sealed types over traditional class hierarchies.
A well-crafted CLI application consists solely of business logic expressed through modern Java
features, with zero external dependencies. The code is as simple, elegant, and understandable
as possible, choosing the approach with the fewest lines of code when multiple options exist.
Business Components
In a CLI application, business components are source files organized by domain responsibility.
Each file uses unnamed classes with top-level methods, avoiding traditional class or interface
declarations. The boundary is the main entry point (App.java), controls contain business logic
as top-level methods, and entities are modeled as records, enums, and sealed types.
Boundary
The boundary is the application's entry point, typically an App.java file with an instance
void main() or void main(String... args) method. It handles argument
parsing, delegates to control logic, and formats output for the terminal.
Entry Point: Instance void main() — not static (unnamed classes do not use static methods)
Arguments: Use void main(String... args) only when args are needed
Output: Use IO.println() or IO::println as method reference — never System.out.println()
Thin Design: Parse input, delegate to controls, format output
Version Tracking: Maintain a String version = "YYYY-MM-DD.N"; instance variable
Control
Controls are top-level methods in unnamed classes that contain business logic. Complex boolean
conditions are extracted into named predicate methods. Non-trivial calculations become named
methods. Inline lambda predicates are extracted into explaining methods and used as method
references.
Top-Level Methods: Business logic as methods in unnamed classes, not in traditional classes
Named Predicates: Extract complex conditions into boolean isEligible() style methods
Method References: Prefer .filter(this::isSkillFile) over inline lambdas
Named Calculations: Extract formulas into methods so call sites read as intent
Functional Fields: Bind behavior to data with Runnable, Consumer, or lambda fields in records
Entity
Entities are modeled using records, enums, and sealed types — never traditional classes or
interfaces. They represent domain data with behavior bound directly to the data structures
through functional fields and pattern matching.
Records: Immutable domain objects with optional functional fields (e.g., record Action(String name, Runnable run))
Sealed Types: Constrained type hierarchies for domain modeling with exhaustive pattern matching
Enums: Business states and categories with behavior
No Classes: Do not create classes or interfaces — use records, enums, and sealed types only
Principles
Zero Dependencies
No Maven, no Gradle, no external libraries. Built with zb (Zero Dependencies Builder)
and the Java platform alone. You know when you need a dependency.
Unnamed Classes
No class or interface declarations. Use top-level methods, records, enums, and sealed types.
No package declarations, no static methods.
Simplest Possible Code
Always choose the simplest API. When multiple approaches exist, use the one with the fewest
lines. Inline single-use variables. Extract repeated literals into constants.
Modern Java 25
Records, sealed types, pattern matching, module imports, IO.println(),
var declarations, and character literals used naturally throughout.
Code Style
Module Imports: Always use import module java.net.http; — never individual type imports
No java.base Imports: The java.base module is automatically available
var Declarations: Use var for local variables where the type is obvious
Character Literals: Write '\n' not 10, define int ESC = '\033' instead of inlining 27
Compact Imports: No blank lines between imports, only import what is used
Colored Output: Use zcl for ANSI color terminal output