My first 6 months with Beancount: lessons learned and workflow tips

My Journey from Spreadsheets to Beancount

Six months ago, I was tracking my finances in Google Sheets with formulas everywhere and manual categorization. After discovering Beancount, I made the switch and haven’t looked back. I want to share my journey, mistakes I made, and the workflow I’ve settled into.

Why I Switched from Spreadsheets

My spreadsheet system was breaking down:

  • 40+ sheets with copy-pasted formulas (nightmare to maintain)
  • No validation: I made data entry errors constantly
  • Investment tracking: Impossible to track cost basis properly
  • Historical analysis: Each month I’d accidentally overwrite past data
  • Tax time: Spent 2 weeks manually aggregating everything

I briefly considered GnuCash (GUI-based) but the database felt like a black box. I wanted my data in plain text, version-controlled, and future-proof.

Month 1: The Learning Curve

Week 1: Installation and Setup

# Installing Beancount + Fava on macOS
pip3 install beancount fava

# Create directory structure
mkdir -p ~/finances/{documents,imports,prices}
touch ~/finances/main.beancount

I started with the absolute minimum:

; main.beancount - Day 1

option "title" "Personal Finances"
option "operating_currency" "USD"

; Opening balances
2024-05-01 open Assets:Bank:Checking
2024-05-01 open Expenses:Groceries
2024-05-01 open Income:Salary

; First transaction ever
2024-05-01 * "Whole Foods"
  Expenses:Groceries    67.43 USD
  Assets:Bank:Checking -67.43 USD

Mistake #1: I tried to import 5 years of history on Day 1. Error overload! Instead, I should have:

  • Started with just ONE WEEK of current transactions
  • Learned the syntax by manually entering expenses
  • Only then tackled historical import

Week 2: File Organization

After a week of everything in one file, I reorganized:

~/finances/
├── main.beancount           # Main entry point
├── accounts.beancount        # Account definitions
├── 2024/
│   ├── 01-january.beancount
│   ├── 02-february.beancount
│   └── ...
├── 2023/
│   └── ...
├── prices/
│   └── prices.beancount      # Price directives
└── documents/
    ├── receipts/
    └── statements/

main.beancount became just includes:

option "title" "Personal Finances"
option "operating_currency" "USD"

include "accounts.beancount"
include "2023/*.beancount"
include "2024/*.beancount"
include "prices/prices.beancount"

accounts.beancount has all my account definitions:

; Bank accounts
2024-01-01 open Assets:Bank:Checking     USD
2024-01-01 open Assets:Bank:Savings      USD

; Credit cards
2024-01-01 open Liabilities:CreditCard:Chase  USD

; Expenses (organized by category)
2024-01-01 open Expenses:Housing:Rent
2024-01-01 open Expenses:Food:Groceries
2024-01-01 open Expenses:Food:Dining
2024-01-01 open Expenses:Transport:Gas
; ... etc

; Income sources
2024-01-01 open Income:Salary:TechCorp

; Equity
2024-01-01 open Equity:Opening-Balances

Week 3-4: Daily Workflow Development

I tried different workflows and settled on this:

Daily (5 minutes):

# Open current month's file
code ~/finances/2024/10-october.beancount

# Add transactions from today (from phone notes or receipts)
# Run validation
bean-check ~/finances/main.beancount

# Quick visual check in Fava
fava ~/finances/main.beancount
# Open http://localhost:5000

I keep a note on my phone for quick expense capture:

10/30 Coffee shop $4.50
10/30 Lunch $12
10/31 Gas $45

Then transcribe to Beancount in the evening.

Month 2-3: Building Good Habits

Balance Assertions: Game Changer

This was the feature that made everything click:

; Every week, I check my bank balance and add:
2024-10-31 balance Assets:Bank:Checking  2847.32 USD

; If this doesn't match, bean-check FAILS
; This catches:
; - Missing transactions
; - Typos in amounts
; - Wrong account mappings

I set a weekly calendar reminder to add balance assertions. Takes 2 minutes, catches errors immediately.

Common Beginner Pitfalls I Hit

Pitfall #1: Wrong account signs

; WRONG - This doesn't balance!
2024-05-15 * "Paycheck"
  Assets:Bank:Checking   3000.00 USD
  Income:Salary          3000.00 USD  ; Should be NEGATIVE!

; CORRECT
2024-05-15 * "Paycheck"
  Assets:Bank:Checking   3000.00 USD
  Income:Salary         -3000.00 USD

Income has negative balances because it’s on the right side of the accounting equation. Took me 3 weeks to internalize this!

Pitfall #2: Forgetting commodity declarations

; This errors out:
2024-06-01 * "Buy stock"
  Assets:Investments   10 AAPL {150.00 USD}  ; Error: AAPL not declared
  Assets:Cash         -1500.00 USD

; Need to add to accounts.beancount:
2024-01-01 commodity AAPL
  name: "Apple Inc."

Pitfall #3: Account type confusion

; WRONG - checking account is not an expense!
2024-06-10 open Checking  ; Error: Must start with account type

; CORRECT
2024-06-10 open Assets:Bank:Checking

Month 4: Integration with Real Life

Using Fava Daily

I keep Fava running all the time now:

Home page: Quick net worth overview

  • Assets: $45,230
  • Liabilities: -$8,450
  • Net Worth: $36,780

Income Statement: Month-to-date spending

Expenses:
  Housing:          $1,500
  Food:              $450
  Transport:         $200
  Entertainment:     $120

Query tab: Ad-hoc analysis

-- How much did I spend on dining out this month?
SELECT sum(position)
WHERE account ~ 'Expenses:Food:Dining'
AND month = 10
AND year = 2024

Tax Preparation Integration

I’m not an accountant, but Beancount makes tax prep easier:

Step 1: Mark tax-deductible expenses

2024-08-15 * "Home office desk"
  tax-deductible: "yes"
  tax-category: "home-office"
  Expenses:Equipment   450.00 USD
  Assets:Cash         -450.00 USD

Step 2: Query at tax time

SELECT account, sum(position)
WHERE metadata('tax-deductible') = 'yes'
AND year = 2024

Step 3: Export to CSV for accountant

bean-query main.beancount "SELECT * WHERE ..." -f csv > tax_2024.csv

Month 5-6: Advanced Workflows

Monthly Reconciliation Process

First weekend of each month (30 minutes):

  1. Download statements from all accounts
  2. Add balance assertions for end of previous month
  3. Run bean-check - fix any errors
  4. Review in Fava:
    • Income vs Expenses (am I saving enough?)
    • Budget vs Actual (using custom BQL query)
    • Net worth trend (is it growing?)
  5. File statements in documents/ folder

Quarterly Review

Every 3 months (1 hour):

  • Investment performance: Compare cost basis to current value
  • Spending trends: Are any categories growing unexpectedly?
  • Goal tracking: Am I on track for savings goals?
  • Account cleanup: Close any unused accounts, consolidate

Version Control

cd ~/finances
git init
git add .
git commit -m "Initial commit"

# Daily commits (automated)
git add -A
git commit -m "Update $(date +%Y-%m-%d)"
git push

I have a private GitHub repo. Benefits:

  • History: Can see my finances on any date
  • Backup: Don’t lose years of data
  • Sync: Access from laptop and desktop

Tools I Use Daily

  1. VS Code with Beancount extension (syntax highlighting, autocomplete)
  2. Fava (always running at http://localhost:5000)
  3. bean-check (validate after every change)
  4. Apple Notes (quick expense capture on phone)
  5. Hazel (macOS automation - auto-move bank statements to correct folder)

What I’d Do Differently

If I started over today:

  1. Start smaller: Just track expenses for 2 weeks before anything complex
  2. Learn balance assertions earlier: They’re the killer feature
  3. Use Fava from Day 1: The web UI makes everything clearer
  4. Don’t stress about perfect categorization: You can always recategorize later (it’s plain text!)
  5. Join the community earlier: The Google Group is incredibly helpful

Results After 6 Months

  • Complete financial picture: Every dollar tracked
  • Investment tracking: Accurate cost basis for taxes
  • Time spent: 15 min/week (vs 2-3 hours/month with spreadsheets)
  • Confidence: I know exactly where my money is
  • Tax prep: From 2 weeks → 2 hours (just export and hand to accountant)

Questions I Still Have

  1. Advanced budgeting: How do others implement budgets in Beancount?
  2. Mobile apps: Any good iOS apps for quick transaction entry?
  3. Forecasting: Can I use Beancount data to forecast future cash flow?

If you’re considering Beancount, my advice: Just start. Don’t wait for the perfect setup. Create a file, add one transaction, and build from there. The strictness that seems intimidating at first becomes your best friend.

Happy to answer questions about the beginner experience!

Congrats on 6 months! Your journey mirrors mine from 2019. I want to address your questions and share some workflow optimizations I’ve developed.

Answering Your Questions

Q1: Advanced Budgeting in Beancount

There are several approaches. Here’s mine:

Option A: Using the budget plugin (official)

plugin "beancount.plugins.forecast"

; Define expected transactions
2024-01-01 * "Monthly rent budget" ^budget-monthly
  Expenses:Housing:Rent   1500.00 USD
  Assets:Budget          -1500.00 USD

; Then compare actual vs budget with BQL

Option B: My approach using metadata and BQL

I create a separate budgets.beancount file:

; budgets.beancount

* budget-monthly: {
  'Expenses:Food:Groceries': 400,
  'Expenses:Food:Dining': 200,
  'Expenses:Transport:Gas': 150,
  'Expenses:Entertainment': 100,
  'Expenses:Shopping': 200
}

Then use a custom Python script to compare:

# budget_report.py

from beancount import loader
from beancount.query import query_execute
import yaml

# Load budgets
with open('budgets.yaml') as f:
    budgets = yaml.load(f)

# Load ledger
entries, _, options = loader.load_file('main.beancount')

# Query actual spending for current month
query = """
SELECT account, sum(position)
WHERE account ~ 'Expenses:'
AND month = MONTH(TODAY())
AND year = YEAR(TODAY())
GROUP BY account
"""

rtypes, rows = query_execute.execute_query(entries, options, query)

# Compare and report
print(f"{'Category':<40} {'Budget':>10} {'Actual':>10} {'Diff':>10}")
print("-" * 70)

for account, budget in budgets['monthly'].items():
    actual = 0
    for row in rows:
        if row.account == account:
            actual = float(row.sum_position.get_only_position().units.number)
            break
    
    diff = actual - budget
    status = "🔴" if diff > 0 else "🟢"
    print(f"{status} {account:<38} ${budget:>9.2f} ${actual:>9.2f} ${diff:>9.2f}")

Run monthly: python budget_report.py

Option C: Fava Extensions

Install fava-envelope plugin for envelope budgeting:

pip install fava-envelope

Add to main.beancount:

2024-01-01 custom "fava-extension" "fava_envelope"

Q2: Mobile Apps for Quick Entry

iOS Options:

  1. Fava mobile web interface: Works great on iPhone

    • Access Fava from phone browser
    • Add transactions via web form
    • Requires Fava running on accessible server
  2. Shortcuts + Drafts: My current setup

    iOS Shortcut:
    - Ask for: Payee, Amount, Category
    - Format as Beancount transaction
    - Append to Drafts
    - Sync to Desktop via iCloud
    
  3. Beancount Mobile (third-party, in development)

My workflow:
I use Apple Notes for quick capture, then batch-transcribe daily. Takes 5 minutes:

Notes (captured on phone):
10/31 Coffee $4.50 Starbucks
10/31 Lunch $12 Chipotle
11/01 Gas $45 Shell

Evening transcription:
2024-10-31 * "Starbucks"
  Expenses:Food:Coffee   4.50 USD
  Liabilities:CreditCard

Q3: Forecasting Future Cash Flow

Yes! Here’s how I do it:

# forecast.py

from beancount import loader
from beancount.core import data, amount
from datetime import date, timedelta
from decimal import Decimal

entries, _, options = loader.load_file('main.beancount')

# Calculate average monthly expenses (last 6 months)
# ... query logic ...

# Project forward 12 months
current_balance = Decimal('25000')  # Current checking balance
monthly_income = Decimal('5000')
avg_monthly_expenses = Decimal('3200')

print("Cash Flow Forecast (12 months)")
print(f"{'Month':<15} {'Income':>10} {'Expenses':>10} {'Balance':>10}")
print("-" * 50)

for i in range(12):
    month_date = date.today() + timedelta(days=30*i)
    current_balance += monthly_income - avg_monthly_expenses
    print(f"{month_date:%Y-%m}        ${monthly_income:>9.2f} ${avg_monthly_expenses:>9.2f} ${current_balance:>9.2f}")

For more sophisticated forecasting, check out:

Workflow Optimizations

Automation I’ve Added

1. Automatic statement downloads

I use ofxget (Python library) with a cron job:

# download_statements.sh (runs daily at 6am)

ofxget --institution bank_of_america statement   --output ~/finances/imports/boa_$(date +%Y%m%d).ofx

# Then beancount-import processes automatically

2. Price updates

# update_prices.py (cron: daily at market close)

import yfinance as yf
from datetime import date

symbols = ['AAPL', 'GOOGL', 'VTSAX', 'BND']

with open('prices/prices.beancount', 'a') as f:
    for symbol in symbols:
        ticker = yf.Ticker(symbol)
        price = ticker.history(period='1d')['Close'].iloc[-1]
        f.write(f"{date.today()} price {symbol} {price:.2f} USD
")

3. Git auto-commit

# ~/finances/.git/hooks/post-commit

#!/bin/bash
# After manual commit, auto-push to backup
git push origin main

Advanced File Organization

After 3 years, my structure evolved:

~/finances/
├── main.beancount
├── config/
│   ├── accounts.beancount
│   ├── commodities.beancount
│   └── plugins.beancount
├── transactions/
│   ├── 2022/
│   ├── 2023/
│   └── 2024/
│       ├── manual/           # Hand-entered
│       │   ├── 01.beancount
│       │   └── 02.beancount
│       └── imports/          # Auto-imported
│           ├── chase.beancount
│           └── boa.beancount
├── investments/
│   ├── lots.beancount        # Investment purchase tracking
│   └── dividends.beancount
├── prices/
│   └── prices.beancount      # Auto-updated
├── reports/
│   ├── budget_report.py
│   └── tax_summary.py
└── documents/
    ├── receipts/
    └── statements/
        └── 2024/
            ├── 01-january/
            └── 02-february/

main.beancount is just includes:

include "config/*.beancount"
include "transactions/*/*.beancount"
include "investments/*.beancount"
include "prices/prices.beancount"

Time-Saving Shortcuts

1. Text expansion (using TextExpander or Keyboard Maestro)

Shortcut: ,tx
Expands to:
2024-10-31 * ""
  Expenses:
  Assets:

2. Common transaction templates

; templates.txt

; Grocery shopping
YYYY-MM-DD * "Whole Foods"
  Expenses:Food:Groceries   XX.XX USD
  Liabilities:CreditCard:Chase

; Gas station
YYYY-MM-DD * "Shell"
  Expenses:Transport:Gas   XX.XX USD
  Liabilities:CreditCard:Chase

Copy, fill in date/amount, paste into ledger.

3. VS Code snippets

// .vscode/snippets.json
{
  "Transaction": {
    "prefix": "txn",
    "body": [
      "${1:$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE} * "${2:payee}"",
      "  ${3:Expenses:}   ${4:0.00} USD",
      "  ${5:Assets:Cash}"
    ]
  }
}

Lessons from 3+ Years

Don’t over-engineer early: Your workflow from month 1-2 is perfect for beginners. I see people trying to automate everything on day 1 and getting overwhelmed.

Balance assertions are non-negotiable: Weekly is good, I do it every paycheck (biweekly). Catches errors before they compound.

Plain text is the superpower: I’ve migrated my ledger through 3 computers, 2 operating systems, and it just works. Try doing that with Quicken!

Community knowledge: The Google Group has solved every weird edge case I’ve encountered. Don’t hesitate to ask.

Great post! Your 6-month experience will help so many beginners.

Excellent workflow guide! I want to add some resources and documentation pointers that helped me when I was at your stage.

Essential Documentation

Your questions about budgeting, mobile apps, and forecasting are common. Here’s where to find answers:

Official Beancount Documentation

  1. Command-Line Accounting Cookbook: Command Line Accounting Cookbook - Beancount Documentation

    • Real-world scenarios (budgets, forecasts, tracking loans)
    • Complete examples you can copy
    • This answered my budgeting question
  2. Beancount Cheat Sheet: Beancount Cheat Sheet - Beancount Documentation

    • Quick syntax reference
    • Common transaction patterns
    • Print and keep next to your desk!
  3. Beancount Scripting & Plugins: Beancount Scripting Plugins - Beancount Documentation

    • How to automate workflows
    • Using Beancount as a Python library

Community Resources

Awesome Beancount: GitHub - wzyboy/awesome-beancount: Beancount tips and tricks

This is THE central resource. Sections include:

  • Editor support: VS Code, Vim, Emacs extensions
  • Mobile solutions: Apps and workflows
  • Importers: Bank-specific import tools
  • Converters: Migrate from Mint, YNAB, Quicken
  • Blog posts: Real user experiences

Google Group: https://groups.google.com/g/beancount

  • Active community (Martin Blais still responds!)
  • Search before asking—most questions answered
  • Monthly “show your workflow” threads

Deep Dive: Tax Preparation

You mentioned tax prep improved from 2 weeks → 2 hours. Here’s the detailed workflow I use:

Tax-Related Metadata

; Throughout the year, tag tax-relevant transactions

2024-03-15 * "Home office monitor"
  tax-year: "2024"
  tax-form: "Schedule C"
  tax-category: "office-equipment"
  tax-deductible: "100"
  receipt: "link://dropbox/receipts/2024-03-15-monitor.pdf"
  Expenses:Equipment   450.00 USD
  Assets:Cash         -450.00 USD

2024-06-20 * "Mortgage interest"
  tax-year: "2024"
  tax-form: "Schedule A"
  tax-line: "8a"
  Expenses:Housing:Interest   1200.00 USD
  Assets:Bank:Checking       -1200.00 USD

Year-End Tax Summary Script

# tax_summary.py

from beancount import loader
from beancount.query import query_execute
from collections import defaultdict

entries, _, options = loader.load_file('main.beancount')

# Group by tax form
tax_data = defaultdict(lambda: defaultdict(list))

for entry in entries:
    if hasattr(entry, 'meta'):
        if 'tax-form' in entry.meta:
            form = entry.meta['tax-form']
            category = entry.meta.get('tax-category', 'Other')
            
            for posting in entry.postings:
                if posting.account.startswith('Expenses:'):
                    amount = posting.units.number
                    tax_data[form][category].append({
                        'date': entry.date,
                        'payee': entry.payee,
                        'amount': amount,
                        'receipt': entry.meta.get('receipt', 'N/A')
                    })

# Generate tax report
for form, categories in tax_data.items():
    print(f"
{form}")
    print("=" * 70)
    
    for category, items in categories.items():
        total = sum(item['amount'] for item in items)
        print(f"
{category}: ${total:,.2f}")
        
        for item in items:
            print(f"  {item['date']} {item['payee']:<30} ${item['amount']:>10.2f}")
            if item['receipt'] != 'N/A':
                print(f"    Receipt: {item['receipt']}")

Run in December: python reports/tax_summary.py > tax_2024.txt

Hand this to your accountant—they’ll love you.

Integration with Tax Software

TurboTax CSV Export:

# turbotax_export.py

query = """
SELECT 
  date,
  payee,
  account,
  sum(position) as amount,
  metadata('tax-category')
WHERE metadata('tax-year') = '2024'
AND account ~ 'Expenses:'
"""

bean-query main.beancount "$query" -f csv > turbotax_import.csv

Import directly into TurboTax Business.

Mobile Workflow: Detailed Setup

@practical_adviser mentioned iOS Shortcuts. Here’s my complete setup:

iOS Shortcut for Transaction Capture

  1. Create Shortcut (Shortcuts app)

    • Name: “Log Expense”
    • Inputs:
      • Ask: “Payee?”
      • Ask: “Amount?” (number)
      • Choose from list: [Groceries, Dining, Gas, Shopping, Other]
  2. Format as Beancount

    Text:
    {Current Date YYYY-MM-DD} * "{Payee}"
      Expenses:{Category}   {Amount} USD
      Liabilities:CreditCard
    
  3. Append to Note

    • Save to “Beancount Inbox” note in Apple Notes
    • Syncs via iCloud
  4. Desktop Processing

    • Open Apple Notes
    • Copy transactions
    • Paste into monthly file
    • Clean up categories/accounts

Android Alternative

Use Tasker or IFTTT:

  • Voice command: “OK Google, log expense”
  • Append to Google Keep note
  • Desktop: Copy from Keep to .beancount file

Advanced: Using Beancount with Obsidian

I recently integrated Beancount with Obsidian (note-taking app):

# Daily Note - 2024-10-31

## Finances
```beancount
2024-10-31 * "Coffee shop"
  Expenses:Food:Coffee   4.50 USD
  Assets:Cash           -4.50 USD

Thoughts

Spending on coffee is getting out of hand…


Then use a script to extract beancount blocks:
```python
# extract_from_obsidian.py

import re
import glob

vault_path = "~/Documents/Obsidian/"
output_path = "~/finances/2024/from-obsidian.beancount"

beancount_blocks = []

for note in glob.glob(f"{vault_path}/**/*.md", recursive=True):
    with open(note) as f:
        content = f.read()
        # Extract ```beancount blocks
        blocks = re.findall(r'```beancount
(.*?)
```', content, re.DOTALL)
        beancount_blocks.extend(blocks)

with open(output_path, 'w') as f:
    f.write('

'.join(beancount_blocks))

Learning Resources

Video tutorials:

  • “Plain Text Accounting with Beancount” (YouTube series by Alex Johnstone)
  • Martin Blais’s PyCon talks on Beancount design

Blog posts:

  • “Complete Guide to Beancount” by Alex Johnstone
  • “Five Years of Beancount” by wzyboy
  • “Beancount: Double Entry Accounting for Developers” on Hacker News

Example ledgers:

Common Workflow Questions

Q: How do you handle splits at restaurants?

2024-10-31 * "Dinner with Alice (split)" #split
  Expenses:Dining              30.00 USD  ; My half
  Assets:Receivable:Friends    30.00 USD  ; Alice owes me
  Liabilities:CreditCard      -60.00 USD  ; Total bill

2024-11-05 * "Alice Venmo repayment"
  Assets:Venmo                30.00 USD
  Assets:Receivable:Friends  -30.00 USD

Q: Handling cash withdrawals?

; Step 1: Withdraw cash
2024-10-31 * "ATM withdrawal"
  Assets:Cash              100.00 USD
  Assets:Bank:Checking    -100.00 USD

; Step 2: Spend cash
2024-11-01 * "Farmers market"
  Expenses:Food:Groceries   25.00 USD
  Assets:Cash              -25.00 USD

; Balance assertion ensures cash tracking is accurate
2024-11-01 balance Assets:Cash  75.00 USD

Q: International travel expenses?

; Track in foreign currency
2024-09-15 * "Hotel in Tokyo"
  Expenses:Travel:Accommodation   15000 JPY @ 0.0067 USD
  Liabilities:CreditCard         -100.50 USD

; Price directive for exchange rate
2024-09-15 price JPY  0.0067 USD

Final Tips

  1. Don’t aim for perfection: Your ledger will evolve. That’s OK.
  2. Balance assertions weekly: Non-negotiable for data integrity.
  3. Join the community: Google Group, Reddit r/plaintextaccounting
  4. Read others’ ledgers: GitHub has many public examples
  5. Document your workflow: Future you will thank you

Great thread! Your 6-month journey shows the realistic learning curve. Most people are productive within 1-2 months if they start small like you did.