A developer's journey building a Rust CLI tool reveals why 'simple' projects often become complex design lessons, covering parser implementation trade-offs, AI coding agents, and maintainability challenges.

Building a "simple" developer tool often becomes an exercise in confronting hidden complexity—a lesson painfully learned when attempting to create a CLI-based API testing tool in Rust. What began as a weekend project stretched into multiple weekends, revealing fundamental truths about software design constraints. The initial premise seemed straightforward: create a Postman-like experience for the terminal with a custom domain-specific language (DSL) for defining tests. But the journey exposed critical decisions around parsing architecture, tooling choices, and the seductive danger of momentum over maintainability.
The Parser Paradox: From Naive Implementation to Grammar-Driven Solutions
The first major hurdle emerged when designing the DSL parser. The initial approach—a handwritten parser—functioned but created maintenance debt. As the input format evolved, changes became increasingly hazardous. Rust's strict type system amplified these challenges; while it prevents entire classes of runtime errors, it also makes iterative parser adjustments laborious. Each modification risked breaking subtle invariants, and the lack of explicit grammar documentation meant the code itself became the specification—a dangerous anti-pattern.
The breakthrough came with adopting pest, a parser generator using Parsing Expression Grammar (PEG) rules. This shifted the paradigm:
- Explicit Structure: The grammar became a living document separate from implementation
- Change Safety: Grammar modifications triggered clear compiler errors at rule boundaries
- Maintainability: Complex parsing logic was abstracted into declarative rules
However, this approach introduced its own trade-offs. The learning curve for PEG syntax was steep, and error messages from generated parsers proved less intuitive than handwritten variants. The win came in long-term sustainability: explicit grammars scale better as DSL complexity grows.
AI Agents: Accelerator or Distraction?
Mid-project, AI coding assistants (GitHub Copilot, Claude, and others) entered the workflow. Their impact was dual-edged:
Pros:
- Rapid prototyping of boilerplate (e.g., HTTP client setup)
- Exploring alternative implementations (e.g., different error-handling strategies)
- Generating documentation scaffolds
Cons:
- Overly verbose or irrelevant suggestions requiring extensive editing
- Tendency to propose complex solutions when simpler ones existed
- Time spent crafting precise prompts often exceeded hand-coding time
The key lesson? AI excels at accelerating known patterns but struggles with novel design constraints. Agents frequently violated the project's core simplicity tenet by suggesting over-engineered abstractions. Knowing when to ignore AI—when to favor deliberate design over generated momentum—became crucial.
Simplicity as a System Design Principle
What does "simplicity" mean for CLI tools? Through building Axotly, several principles crystallized:
- Constraint-Driven Design: Every feature requires justification against core use cases. CLI tools drown in scope creep.
- Explicit Over Implicit: Handwritten parsers hide assumptions; formal grammars make them visible.
- Composition Over Integration: Prefer modular components (e.g., separate parsing and execution) over monolithic structures.
- Toolchain Awareness: Leverage Rust's strengths (cargo, crates) rather than fighting them.


Comments
Please log in or register to join the discussion