Table of Contents
Exec Summary
For business and technical leaders: This post documents practical lessons from building production-ready infrastructure libraries in Mojo, a new systems programming language designed for AI and high-performance computing. Key takeaways:
- Mojo is production-ready for infrastructure libraries: Successfully built a 1,500-line TOML parser with 96 tests, achieving performance suitable for production use.
- Static typing delivers ROI: Explicit type conversions add minor verbosity but provide compile-time safety and clear error messages, reducing debugging time.
- Performance measurement matters: Benchmarking early revealed that "performance concerns" were unfounded and the parser is already fast enough, allowing focus on correctness and usability.
- Open source ecosystem building: Contributing foundational libraries (configuration management, parsing) accelerates Mojo adoption for production workloads.
- Risk mitigation through testing: 96 comprehensive tests across 10 logical groupings provide confidence for production deployment.
Business value: These infrastructure libraries reduce the barrier to adopting Mojo for performance-critical applications while maintaining the developer productivity expected from modern languages.
Series Context
This is the second post in my Mojo library development series:
- Part 1: Building mojo-dotenv - Configuration management (~200 lines Mojo code, 42 tests)
- Part 2: This post - TOML parser (~1,500 lines Mojo code, 96 tests)
- Part 3: Building mojo-asciichart - ASCII charting (~550 lines Mojo code, 29 tests)
The Increase in Code Scale
After recently building the mojo-dotenv package (see Part 1), I took on a more ambitious project: mojo-toml, a native TOML 1.0 parser. While mojo-dotenv taught me the basics of Mojo library development, mojo-toml revealed new challenges and lessons at a different scale:
mojo-dotenv: ~200 lines Mojo code, 42 tests, simple line-based parsingmojo-toml: ~1,500 lines Mojo code, 96 tests, full lexer + recursive descent parser
New Lessons Learned
Here are some of the lessons learned during the development of this package.
1. Static Typing Requires Different API Design
In Python's tomli, you access values directly:
port = config["database"]["port"]
While in Mojo, you need explicit type conversions:
var db = config["database"].as_table()
var port = db["port"].as_int()
The Lesson: Don't try to replicate all of a Python API. Embrace static typing as a feature, not a limitation. The explicit conversions provide valuable type safety and clear errors.
What I learned: Mojo users coming from Rust, C++, or Go will find this natural. The verbosity is minimal compared to the safety gained.
2. Variant Types Need Careful Design
TomlValue, a custom variant structure I defined, can hold any TOML type (string, int, float, bool, array, table).
struct TomlValue:
var value_type: Int # 0=String, 1=Int, etc.
var string_value: String
var int_value: Int
var float_value: Float64
# ... all possible types as fields
The Lesson: Sometimes the "obvious" design in other languages isn't necessarily the right choice in Mojo. Work with the language, not against it.
Performance note: This approach has minimal overhead.
3. Ownership and Copying are Everywhere
Mojo's ownership model meant copying was necessary in places I didn't expect:
# Must copy when iterating and returning
for entry in dict.items():
result[entry.key] = entry.value.copy() # Copy required
The Lesson: Embrace copying where needed. Modern CPUs are fast, and most config parsing happens once at startup. Document why copying happens, it helps users understand the trade-offs.
What I documented: Created docs/PERFORMANCE.md explaining copying behaviour and measuring its actual impact (negligible).
4. Test Organisation Scales Differently
With 96 tests, organisation became critical. I split tests into 10 logical files:
test_lexer.mojo(25) - Tokenisationtest_parser.mojo(10) - Core parsingtest_dotted_keys.mojo(7) - Feature-specifictest_validation.mojo(7) - Error detection- ... and 6 more
The Lesson: Organise by feature/responsibility, not by implementation stage. Makes it easy to find and extend tests. Note that structured testing in Mojo uses the TestSuite framework.
Documentation matters: Created docs/TEST_ORGANIZATION.md explaining the structure helps contributors know where to add tests.
5. Error Messages Need Line/Column Context
Initially, my errors were vague: "Unexpected token", so not particularly useful.
Added position tracking:
struct Position:
var line: Int
var column: Int
fn format_error(msg: String, pos: Position) -> String:
return msg + " at line " + String(pos.line) + ", column " + String(pos.column)
The Lesson: Invest in error messages early. Users will spend more time debugging config files than you'll spend implementing good errors.
6. Benchmarking Reveals Surprises
I was concerned about performance and built a small benchmark suite. The results:
- Parsing overhead: 26 microseconds
- Real pixi.toml: 2 milliseconds
- Table access overhead: 10 microseconds illustrated that initial performace is acceptable.
The Lesson: Measure, don't guess. My "performance concerns" were irrelevant, the parser is already fast enough for any config file use case.
Bonus: Having benchmarks makes it safe to refactor. You know immediately if you've made something slower.
7. Documentation Structure Matters at Scale
Started with everything in README. By release, I had:
README.md- Quick start and essentialsdocs/ROADMAP.md- Planned featuresdocs/PERFORMANCE.md- Benchmarks and design trade-offsdocs/TEST_ORGANIZATION.md- Test structuredocs/TOML_WRITER_DESIGN.md- Future writer design
The Lesson: Move details to separate docs early. README should get users started in 2 minutes. Deep dives go elsewhere.
Lessons That Still Apply
From mojo-dotenv lessons that remained true:
- Python interop enhances validation - Still invaluable, though less critical for a more complex parser.
- pixi for environment management - Flawless experience.
- Test-driven development - Even more important at this scale.
Community and Recognition
One of the most encouraging aspects of working with Mojo has been the responsiveness and support from the Modular team and community. After releasing mojo-dotenv, Modular CEO Chris Lattner took the time to acknowledge the contribution, saying "This is super cool, congratulations!"
This kind of direct engagement from leadership isn't just personally gratifying, it strengthens the entire community. When founders and technical leaders actively recognize and encourage open source contributions, it signals that the ecosystem values infrastructure building and that contributions matter. For those considering contributing to the Mojo ecosystem, this supportive environment makes it a particularly rewarding space to work in.
What's Next
Building mojo-toml taught me that Mojo is ready for non-trivial libraries. The language has its quirks, but they're manageable. The real challenges are the same as any systems programming language: design good APIs, handle errors well, write tests.
Next up: Extend the library to include a TOML writer. That'll hopefully teach me about the reverse problem, generating correct output instead of parsing it.
Try it yourself:
git clone https://github.com/databooth/mojo-toml
cd mojo-toml
pixi run test-all # Run 96 tests
pixi run example-quickstart # See it in action
Or use pixi add mojo-toml after the modular-community PR is approved (should be within a few days of this post).
Building high-performance data and AI services with Mojo at DataBooth. Questions or want to collaborate? Get in touch.