Pivoting with Purpose: Introducing Tool-Driven Development (TDD) with PicoCLI
- Prioritize Tools over UI: Use CLI tools to validate business logic early, avoiding the high cost and wasted effort of building UIs for shifting requirements.
-
Spring + PicoCLI Synergy: Integrating PicoCLI with Spring Boot allows commands to use existing services and dependency injection, making development fast and robust.
-
Bridge the Feedback Gap: CLI tools turn "silent" backend logic into visible, executable results that help stakeholders validate progress without waiting for a front end.
-
Agile "Add, Don't Rewrite": Using a subcommand hierarchy allows the team to pivot—like moving from single to bulk actions—by simply adding new commands to the existing toolbox.
-
Long-Term Utility: These tools evolve from development prototypes into permanent operational assets for support, data cleansing, and troubleshooting.
Ever worked on a project where requirements kept changing and stakeholders expected visible progress before the foundations were fully in place? This is a familiar challenge for many development teams. In one such project, we were responsible for delivering a primarily backend-driven system, with limited opportunities for early business validation and continuously evolving operational workflows.
Our answer? Not a complex UI or lengthy design phase, but a powerful toolbox of command-line interface (CLI) tools built with picocli - a mighty tiny command line interface , seamlessly integrated into our Spring Boot ecosystem. This approach became so fundamental to our agility that we've coined it: Tool-Driven Development (TDD).
TDD, in our context, means prioritising the creation of small, focused, and rapidly deployable CLI tools to validate assumptions, provide immediate feedback, and support the entire project lifecycle—from development and data analysis to support and operational queries.
In this article, we'll explore how PicoCLI empowered this Tool-Driven Development, transforming ambiguity into agility and establishing an indispensable operational toolbox.
The Uncharted Territory: Why TDD Emerged
Our project faced significant headwinds:
Fluid Requirements: Business processes for screen management were undefined. We needed to quickly test hypotheses and observe their real-time implications.
Boring backend Silence: A backend offered no immediate visual feedback. We needed a rapid way for stakeholders to "see something working" and validate assumptions directly.
High Risk of Waste: Building full UI or API layers for uncertain requirements meant potential rework and wasted effort. We needed low-cost, high-flexibility solutions.
Operational Gaps: Beyond initial setup, there was a constant need for ad-hoc tools for queries, support, data cleansing, and direct API interaction.
PicoCLI & Spring: The Engine for Our TDD Toolbox
PicoCLI was our chosen engine for TDD due to its development speed, robust parsing, and focus on functional execution. Its seamless integration with Spring Boot allowed our CLI commands to leverage existing services and dependency injection.
Our Spring configuration for PicoCLI:
@Configuration
public class PicocliConfig {
@Autowired private ApplicationContext applicationContext;
@Bean
public CommandLine.IFactory picocliBeanFactory() {
return new CommandLine.IFactory() {
@Override
public K create(Class cls) throws Exception {
return applicationContext.getBean(cls); // Spring manages command instances
}
};
}
}
---
@Component
@Command(
name = "project-cli",
mixinStandardHelpOptions = true,
version = "1.0",
description = "A command-line interface for backend operations",
subcommands = {
ServiceACommand.class,
ServiceBCommand.class,
ServiceCCommand.class,
DataCommand.class,
ImportCommand.class
},
usageHelpAutoWidth = true
)
public class RootCommand implements Runnable {
private final CommandLine commandLine;
@Autowired
public RootCommand(@Qualifier("picocliBeanFactory") CommandLine.IFactory factory) {
this.commandLine = new CommandLine(this, factory);
}
@Override
public void run() {
commandLine.usage(System.out);
}
}
This setup allowed us to surface fine-grained application logic as executable commands. To support rapid feedback during development, a single-entity command played a central role in validating core workflows:
@Slf4j
@Component
@Command(name = "single", description = "Process a single entity")
@RequiredArgsConstructor
class ProcessSingleCommand implements Runnable {
@CommandLine.Parameters(index = "0", description = "Entity identifier")
private Integer entityId;
private final DomainService domainService;
@Override
public void run() {
domainService.process(entityId);
}
}
Beyond development, our TDD approach created an operational toolbox. The QueryServiceCommand, for example, became essential for support and data analysis:
@Slf4j
@Component
@Command(name = "query", description = "Query domain data")
class QueryServiceCommand implements Runnable {
@CommandLine.Parameters(index = "0", description = "Entity ID")
private Integer id;
private final QueryService queryService;
QueryServiceCommand(QueryService queryService) {
this.queryService = queryService;
}
@Override
public void run() {
try {
prettyPrint(queryService.getDetails(id));
} catch (IOException e) {
log.error("Error retrieving details for ID {}: {}", id, e.getMessage());
System.out.println("Error during query: " + e.getMessage());
}
}
}
This command allowed engineers and support staff to quickly retrieve raw entity details, with pretty-printed JSON output for immediate readability. As the toolbox expanded, we organized related functionality using PicoCLI’s subcommand hierarchy to keep commands discoverable and easy to navigate.
@Component
@Command(
name = "query",
description = "Query-related commands",
subcommands = {
GetCommand.class,
ValidateCommand.class,
RefreshCommand.class
}
)
class QueryCommands implements Runnable { }
@Component
@Command(
name = "process",
description = "Processing commands",
subcommands = {
ProcessBatchCommand.class,
ProcessSingleCommand.class,
ValidateBatchCommand.class
}
)
class ProcessCommands implements Runnable { }
This structure enabled clear and intuitive navigation, allowing commands such as project-cli process single … or project-cli query get 12345.
TDD in Action: Fast Feedback, Rapid Pivots, Rich Toolbox
Our Tool-Driven Development approach provided several critical advantages:
Rapid Prototyping: New hypotheses or workflow adjustments were translated into executable commands within minutes or hours. We extended existing commands with new options or introduced new ones to quickly test alternative configurations and processing strategies.
Immediate Feedback & Progress: CLI tools became our "visible output." Instead of abstract discussions, we could demonstrate direct execution, like "Run this command, and here's the immediate result." This built trust and accelerated validation.
Agile Pivoting: When requirements shifted from processing individual items to supporting bulk, file-based workflows, the existing single-entity command was not discarded. Instead, new bulk and validation commands were introduced as additional subcommands, reusing the same core services. This “add, don’t rewrite” approach enabled fast, low-risk pivots as requirements evolved.
Minimised Waste: By focusing on CLI first, we avoided heavy investment in UIs or APIs that might have become obsolete. We built only what was necessary to test assumptions.
Enduring Operational Toolbox: Beyond initial development, the CLI became indispensable. Commands for data cleansing, ad-hoc reports, API simulations, and troubleshooting (StoreLocatorCommand being a prime example) significantly streamlined operations and support.
The Power of TDD: Tangible Benefits

Tool-Driven Development yielded profound impacts:
Empowered Stakeholders: Business users, support engineers, and data analysts could directly interact with core functionality, seeing immediate results and actively contributing to validation and operations.
Accelerated Decision-Making: Faster feedback loops led to quicker, more informed decisions on process flows and data structures.
Developer Efficiency & Morale: Less rework and clear, achievable goals boosted team productivity and satisfaction.
Streamlined Operations: The comprehensive CLI toolbox dramatically reduced time spent on manual queries, support tasks, and data management.
Lessons Learned: Adopting Tool-Driven Development
Our journey with PicoCLI highlights key principles for adopting TDD:
Embrace "CLI First" (Strategically): For projects with high ambiguity or a need for rapid iteration and feedback, start with CLI tools to validate core logic and gather requirements.
PicoCLI + Spring = Agile Power: This combination builds flexible, testable, and maintainable CLI tools.
Don't Fear Disposable Tools: Some commands are temporary, built to answer specific questions. Their value is in the insight they provide, not their longevity.
Cultivate an Operational Toolbox: Think beyond initial development. PicoCLI excels at creating focused tools for ongoing support, analysis, and automation.
Bridge the Gap: CLI tools effectively bridge the space between backend logic and business validation when traditional UIs are premature or overkill.
Conclusion
Our project, initially defined by uncertainty, transformed into a showcase of Tool-Driven Development thanks to PicoCLI. By enabling us to build a versatile and rapidly deployable toolbox of command-line tools, we provided immediate progress, tested options, pivoted quickly, and empowered various teams with efficient operational capabilities.
