The Plain Text Accounting Dilemma
I’ve been using plain text accounting (PTA) tools for over a decade now—started with Ledger in 2012, migrated to hledger in 2017, and finally settled on Beancount in 2020. After watching the ecosystem evolve, I want to share a deep technical comparison to help others choose the right tool in 2025.
Philosophy: The Core Difference
Understanding the philosophical differences is crucial:
Beancount: Strict Validation & Correctness
- Design goal: Prevent errors through strict validation
- Five account types enforcement: Assets, Liabilities, Income, Expenses, Equity
- Balance assertions required: Forces you to reconcile regularly
- Explicit over implicit: Everything must be declared (commodities, accounts)
- Python-based: Extensible through plugins
Ledger: Maximum Flexibility
- Design goal: Get out of your way, assume you know what you’re doing
- No enforced structure: Define accounts however you want
- Implicit accounts: Just start using them, no declaration needed
- C++ implementation: Fast but harder to extend
- Virtual transactions: Powerful but can lead to confusion
hledger: The Middle Ground
- Design goal: Ledger compatibility with better UX
- Haskell-based: Strong typing, correctness guarantees
- Strict mode available: Can enforce Beancount-like validation
- Better error messages: More beginner-friendly than Ledger
- Active development: Regular releases, responsive maintainer
Data Validation Approaches
This is where the tools diverge most significantly.
Beancount’s Strict Validation
# This will ERROR in Beancount:
2024-01-01 * "Purchase"
Expenses:Food 50.00 USD
Assets:Cash -45.00 USD ; Only -45, not -50!
# Error: Transaction does not balance
Beancount refuses to load files with unbalanced transactions. This strictness catches errors early.
hledger’s Flexible Approach
# This WORKS in hledger (infers the missing amount):
2024-01-01 Purchase
expenses:food $50.00
assets:cash
# hledger infers: assets:cash = -$50.00
hledger allows implicit amounts, but you can enable strict mode with --strict.
Ledger’s Permissive Model
Ledger is similar to hledger but even more permissive. It allows constructs that can be confusing:
# Virtual transactions (only in Ledger):
2024-01-01 Budgeting
(Budget:Food) $500.00
These don’t affect real accounts but can be used for budgeting. Powerful but requires discipline.
Performance Benchmarks (2025)
I tested all three tools with a real-world ledger: 120,000 transactions over 10 years, including investments with lot tracking.
Load Time
Ledger: 0.85 seconds
hledger: 2.3 seconds
Beancount: 3.1 seconds
Winner: Ledger (C++ speed advantage)
Balance Report Generation
Ledger: 0.12 seconds
hledger: 0.18 seconds
Beancount: 0.45 seconds
Winner: Ledger again
BQL Query (Beancount-specific)
Beancount: 0.65 seconds (complex aggregation)
No equivalent in Ledger/hledger. They have reporting, but not SQL-like queries.
Memory Usage
Ledger: 145 MB
hledger: 380 MB
Beancount: 520 MB
Winner: Ledger (most efficient)
Verdict: If you have 100k+ transactions and performance matters, Ledger wins. But for most users (< 50k transactions), the differences are negligible.
Core Operations: Key Differences
Lot Booking and Cost Basis
Beancount (most explicit):
2024-01-15 * "Buy stock"
Assets:Investments:AAPL 10 AAPL {150.00 USD}
Assets:Cash -1500.00 USD
2024-06-01 * "Sell stock"
Assets:Investments:AAPL -10 AAPL {150.00 USD} @ 180.00 USD
Assets:Cash 1800.00 USD
Income:Gains -300.00 USD
Beancount requires you to specify lots explicitly and calculate gains.
hledger (flexible):
2024-01-15 Buy stock
assets:investments:aapl 10 AAPL @ $150.00
assets:cash -$1500.00
hledger tracks cost basis but doesn’t enforce gain calculations. You can query it later.
Ledger (similar to hledger but with lot selection):
2024-06-01 Sell stock
assets:investments:aapl -10 AAPL {2024-01-15} @ $180.00
assets:cash $1800.00
Ledger supports lot selection with dates but requires --lot-dates flag.
Currency Conversions
All three handle multiple currencies, but differ in how they track conversion rates:
- Beancount: Explicit
pricedirectives required - hledger: Can use
Pdirectives or infer from transactions - Ledger: Most flexible, supports complex price expressions
Five Account Types
Beancount: Strictly enforced. Every account MUST start with Assets, Liabilities, Income, Expenses, or Equity.
hledger/Ledger: No enforcement. You can have Accounts:Random:Whatever. This flexibility can be good or bad depending on your discipline.
Plugin Ecosystems and Extensibility
Beancount
- Python plugins: Easy to write, well-documented
- Ingest framework: For importing bank data
- Fava: Beautiful web UI (best in class)
- Awesome Beancount: Large collection of community tools
hledger
- Haskell plugins: Harder to write but type-safe
- hledger-web: Decent web UI
- CSV import rules: Powerful and flexible
- Active ecosystem: Regular updates
Ledger
- C++ plugins: Very difficult to write
- Limited ecosystem: Smaller community
- ledger-web: Exists but unmaintained
When to Choose Each Tool
Choose Beancount if:
- You want strict validation and error prevention
- You need investment tracking with complex lot accounting
- You value BQL for advanced queries
- You want Fava (best web interface)
- You plan to write custom plugins (Python is accessible)
- You’re okay with slower performance (3-4 seconds load time)
Choose hledger if:
- You want Ledger compatibility with better UX
- You need good performance but not Ledger’s speed
- You value excellent documentation
- You want flexible validation (strict mode optional)
- You need multi-period reports (hledger excels here)
- You’re migrating from Ledger
Choose Ledger if:
- You have 100k+ transactions and need maximum speed
- You want maximum flexibility and control
- You’re comfortable with minimal error checking
- You need virtual transactions for budgeting
- You’ve been using it for years (ecosystem knowledge)
2025 Ecosystem State
- Beancount v3: Released in 2024, major performance improvements, better plugin API
- hledger 1.35+: Active development, new features quarterly
- Ledger 3.3: Slow development pace, stable but fewer updates
My Recommendation
For new users in 2025: Start with Beancount. The strict validation will teach you proper accounting, BQL is powerful, and Fava makes it accessible. Once you understand the principles, you can always migrate to hledger or Ledger if needed.
For Ledger veterans: Consider hledger if you want better UX while keeping your workflow. Beancount if you want strictness and plugins.
For performance-critical use: Stick with Ledger. Nothing beats C++ speed.
What’s your experience? Anyone made the migration between these tools?