February 14, 2026. Valentine's Day. I was sitting in my apartment in Kharkiv, staring at a screen that showed:
Invoice #001
Client: [empty]
Amount: [empty]
Status: DRAFT
I had spent two months building LockMargin. I had a working database. I had a PDF exporter. I had a UI that didn't make me want to vomit. But I had never sent a real invoice from it.
Not a test invoice. Not a demo invoice. A real invoice to a real client who owed me real money.
The client was a German design agency. The project was a two-week consulting gig. The amount was €3,200. And the payment terms were 14 days.
I clicked "Create Invoice." The app froze for 3 seconds. Then it crashed.
What Broke
Bug #1: The Currency Symbol
I had hardcoded the currency symbol as "$". Because I'm American now, apparently. The client was German. The invoice showed:
Total: $3,200 EUR
Not "€3,200". Not "3,200 EUR". "$3,200 EUR". Three currency symbols in one line. I stared at the PDF for 10 seconds, then laughed. Then I cried a little.
Fix: I rewrote the currency formatter that night. Not just symbols — proper localization. Decimal separators. Thousands separators. Symbol placement before or after the number depending on the locale. It took 4 hours. I ate cold borscht at 2 AM.
Bug #2: The PDF Font
The PDF exported fine. I opened it. The client's name — "Müller & Schmidt Design" — rendered as "Mller & Schmidt Design". The umlaut disappeared. Because I had embedded a font that didn't support German characters.
I had tested with "John Smith" and "ABC Corp". I had never tested with a real European name.
Fix: Switched to a font with full Unicode support. Added test cases with names containing: ä, ö, ü, ñ, ç, ł, ø, and every other character I could find. The test file looks like a linguistic asylum now.
Bug #3: The VAT Presentation
Germany has 19% VAT. I knew this. I had implemented VAT calculation. The math was correct:
Subtotal: €2,689.08
VAT (19%): €510.92
Total: €3,200.00
But I had displayed it as a single line: "Total (incl. VAT): €3,200.00". No breakdown. No separate line item. No calculation methodology.
The German accountant replied with a 500-word email. Not angry — confused. She needed the VAT shown as a separate line item with the exact calculation method documented. For German tax purposes, the presentation matters as much as the numbers.
I had the numbers right. The presentation was wrong.
Fix: Added "VAT display mode" — inclusive vs. exclusive, with line-item breakdown. Added a note field for calculation methodology. Added country-specific invoice templates. Took 6 hours.
Bug #4: The Email Subject
I exported the PDF. I attached it to an email. I wrote:
Subject: Invoice
The client replied: "Which invoice? From which project? For what period?"
I had built an invoicing tool that generated perfect PDFs and left the email entirely to me. No template. No auto-subject. No client name in the filename.
Fix: Added email templates. Auto-subject: "Invoice #[number] — [project name] — [client name]". Auto-filename: "LockMargin_Invoice_001_MuellerSchmidt_2026-02-14.pdf". Added .eml export (Phase 1 of our Client Communication System).
What Worked
The Database Didn't Lose My Data
Despite the crash, despite the wrong currency, despite everything — when I reopened the app, the invoice was there. Draft status. All data intact. SQLite + WAL mode. Zero corruption. I had spent January agonizing over the database choice. In that moment, I stopped agonizing.
The PDF Looked Professional
Once I fixed the font, the PDF was clean. Header with my logo. Clean table. Proper totals. Terms and conditions footer. It looked like it came from software that cost $500/year, not software I had built in my kitchen.
The Client Paid
I sent the corrected invoice on February 15. The client paid on February 28. Fourteen days exactly. The money hit my account on March 1.
I opened LockMargin. I marked the invoice as "Paid." The dashboard updated. My "Outstanding" widget dropped from €3,200 to €0. My "Revenue This Month" widget jumped to €3,200.
I stared at that number for five minutes. Not because it was a lot of money. Because I had built the thing that showed it to me. And it worked.
What I Learned
You Can't Test Reality
I had 47 test cases. Unit tests. Integration tests. I had tested invoice creation, PDF export, database writes, currency formatting. I had not tested "send a real invoice to a real German client and get paid."
That test doesn't exist in any framework. You have to live it.
Dogfooding Is Not Optional
If I had waited until "v1.0" to use my own product, I would have shipped with "$3,200 EUR" and missing umlauts. Dogfooding isn't a nice-to-have. It's the only way to find the bugs that matter.
The Small Bugs Are the Big Bugs
The database architecture? Solid. The reactive engine? Flawless. The UI framework? Stable. What broke? A currency symbol. A font. An email subject line.
The infrastructure was fine. The details were broken. And details are what users see.
I Almost Deleted It Twice
The first time was when I saw "$3,200 EUR" on the invoice. I thought: "This is garbage. No one will trust this. I'm not a developer — I'm a fraud." I closed the laptop and didn't open it for 6 hours.
The second time was when the German accountant sent her 500-word email about VAT compliance. I thought: "I'm not qualified for this. I don't know German tax law. I should go back to consulting." I stared at the "Delete Project" button for 30 seconds.
I didn't delete it either time. I fixed it. Because I needed the €3,200. And because I was too stubborn to quit.
The Numbers
| Metric | Value |
|---|---|
| Time to first real invoice | 2 months (Dec 2025 – Feb 2026) |
| Bugs found during dogfooding | 4 critical, 12 minor |
| Time to fix critical bugs | 14 hours total |
| Client payment time | 14 days (exactly on terms) |
| My stress level, pre-fix | 9/10 |
| My stress level, post-payment | 3/10 |
| Times I almost deleted the project | 2 |
| Hours staring at "Paid" status | 0.08 (5 minutes) |
What's Next
Post #2: Why I Chose SQLite Over PostgreSQL — or how I learned that building secure software is not the same as thinking your software is secure.
Post #4: How We Test Encryption Without a Security Team — or how I learned that building secure software is not the same as thinking your software is secure.
Follow the entire series at Building LockMargin.