Tuulbelt Design Principles
These principles guide what gets built, how it's built, and what doesn't belong.
1. Single Problem Per Tool
Each tool solves one problem and solves it well.
Good: "Detect unreliable tests by running them N times" Bad: "Complete test framework with mocking, fixtures, and reporting"
If you're adding features and the scope is growing, it's time to split into a new tool.
2. Zero External Dependencies (Standard Library Only)
No npm install, no cargo add, no runtime dependency management required.
Exception: Development dependencies (TypeScript compiler, test runners) are okay if they don't ship with the tool.
Rationale: Tools that don't require dependency resolution outlive language trends. They're more portable, more maintainable, and less fragile.
3. Portable Interface
Tools communicate via:
- CLI with flags and stdin/stdout
- Files (plain text, JSON, binary)
- Environment variables
- Sockets (TCP, UDP)
NOT via:
- Proprietary package APIs
- Internal state management
- Plugin systems
- Configuration frameworks
Rationale: Interfaces that work across languages, shells, and systems are reusable.
4. Composable (Pipes, Not Plugins)
Tools should chain together via pipes:
tool-a | tool-b | tool-cNOT via:
- Plugin systems
- Hook APIs
- Event listeners
Rationale: Composition over extension keeps tools independent.
5. Independently Cloneable
Each tool is its own GitHub repository.
Users should be able to:
git clone https://github.com/tuulbelt/<tool-name>.git
cd <tool-name>
npm test && npm run buildWithout needing the meta repo or any other tool.
6. Proven Implementation, Not Moonshots
Before building:
- Problem must be real (you hit it, or it's documented in production)
- Solution must have a clear implementation path
- No "it would be cool if..." without proof it works
- No "works 80% of the time" solutions
Red flags:
- "I think this would be useful"
- "It's like X but better" (without explaining why X is broken)
- Complex state management or learning curve
- Vague scope ("improve developer experience")
7. Documentation Over Configuration
If a tool needs explanation, document it. Don't add config complexity.
Good:
-f, --format <json|text> Output format (default: json)Bad:
--config my-config.json # 50 keys, most optional8. Fail Loudly, Recover Gracefully
- Errors should be explicit and helpful
- Stack traces should include context (file, line, operation)
- Degradation should be obvious, not silent
9. Test Everything, Ship Nothing Untested
- Unit tests for core logic
- Integration tests for CLI behavior
- Edge case coverage (empty input, malformed input, concurrent access)
- Tests run in CI on every commit
10. Version Independently
Each tool has its own version and changelog. The meta repo has no version.
Use semantic versioning:
0.1.0— First release, unstable API1.0.0— Stable API, production-ready2.0.0— Breaking change
What Doesn't Belong in Tuulbelt
- Frameworks (anything that tries to be your whole app)
- Heavy abstractions (frameworks disguised as utilities)
- Language-specific hacks (unless the problem is language-specific)
- Vaporware (things that work 80% of the time)
- Opinionated workflows (unless they're optional)
- Things that require significant setup (complexity = fragility)
Examples of Tools That Fit
"Run tests N times, report flaky ones" (single problem, clear output)
"Convert YAML to JSON" (single problem, standard I/O)
"Manage concurrent test port allocation" (single problem, solves real issue)
"Generate FFI boilerplate from C headers" (single problem, code generation)
Examples of Tools That Don't Fit
"Complete testing framework" (too broad)
"Multi-format data transformer with plugin system" (framework)
"Universal configuration management" (scope creep)
"AI-powered development assistant" (vaporware)
"Framework for building frameworks" (meta-framework)
Before Building a New Tool, Answer:
- What single problem does it solve?
- How would I test it end-to-end?
- Can it run without installing dependencies?
- Can it be used from any programming language?
- Have I hit this problem in real code?
- Is there a proven implementation path, or am I inventing something new?
If you can't answer clearly, wait. Revisit the idea later.