Arxiva
A Case Study in Knowing When to Rewrite
A desktop app that automates Andorra's annual accounts filing. Accountants fill one Excel workbook; the app generates the XML files the government portal expects, uploads them, the accountant reviews and submits. Two hours of manual form-filling became a few clicks. The project went through two completely different architectures before reaching the one that ships.
Role
Solo Developer
Tools
Tauri 2, JavaScript, Python, Playwright
WCAG 2.1 AA
WCAG 2.1 AA
i18n
5 Languages (CA, ES, FR, EN, PT)

The Problem:
Andorran accounting firms spend approximately 2 hours per client filing annual accounts through the government's Orbeon XForms portal. Four different form types (B1-B4) with ~2,000 total fields across sheets that accountants modify for each client. The first version was a Python script: read the Excel, write XML, done. It worked for one client's data - and broke immediately on the second accountant's workbook.
Act I - Three Excel Strategies:
Three different forms, three different data structures, three different approaches. B1/B2 uses a heuristic (notesNum + 1) with an override table for out-of-sequence fields. B3 has 15 sheets and 500+ rows - uses anchor-based lookup searching for stable labels like '5.1.1.' and reading at known offsets. B4 is rigid with hardcoded coordinates. The pragmatic choice over architectural purity.
Act II - The Browser Detour:
The uploaded XML only populated a third of the fields. The rest (Sí/No radios, textareas, repeating grids, country dropdowns) were filled manually. So we built a Playwright sidecar - ~1,300 lines of Python, compiled into a 50MB PyInstaller binary, bundled with Tauri. It worked end-to-end. Then bug reports started: 9 minutes on old Windows laptops, Chrome CSS changes breaking selectors, RAM-starved machines timing out on Orbeon loading spinners. Every workaround made the automation more defensive and less fast.
The Quiet Observation
During a routine round-trip test (upload XML, export XML, diff) we noticed fields we hadn't filled via XML were appearing in the export with values we'd typed via the browser. The portal was storing them under tag names we hadn't been using. Reverse-engineering the export revealed 1,550 tags covering every field - including every Sí/No, date, dropdown, and repeating row. We'd been automating the browser to do something the portal already accepted as a file upload.
Act III - The Rewrite:
Deleted ~1,100 lines of Python sidecar. Every CSS selector, every timing workaround, every per-step field-filling handler - gone. Kept the anchor-based Excel extractor (ported to JavaScript) and the Tauri shell. The sidecar reduced to ~200 lines: two HTTP file uploads and a few wait_for_load_state calls. From 9 minutes on old laptops to 4 seconds. Impossible to crash on Chrome updates. The browser automation scaffolding stays in the codebase as reusable infrastructure for future form types that don't yet support full XML import.
9 min
Before (old laptop)
4 sec
After (same laptop)
~1,100
Python lines deleted
1,550
XML tags discovered
The Lesson:
The lesson isn't 'rewrite your code.' It's: if the thing you're working around is load-bearing, your architecture is wrong. Every workaround we added to the Playwright version was load-bearing on a fragile foundation. Older machines exist. The portal is the adversary. Root cause beats workaround. When something fails now, the user has an XML file on disk and an XSD - two commands to validate.
Tech Stack:
- Tauri 2 (Rust) shell - 5MB vs Electron's 100MB
- Vanilla JS + CSS + Vite - no framework overhead for a single-page tool
- XLSX.js (SheetJS) - browser-compatible Excel parsing
- PyInstaller sidecar (~200 lines) - reliable CDP for two file uploads
- HMAC-SHA256 license system - offline verification, no server
Accessibility:
Full WCAG 2.1 AA compliance. Programmatic contrast checker against every color combination. Accent text 1.90:1 to 6.36:1. Disabled button 1.46:1 to 9.75:1. Focus indicators, ARIA labels, 44px touch targets. Accent colors that look great on screen often fail contrast - the teal ships exclusively on backgrounds and borders, never text.
What I Learned:
First time shipping a downloadable desktop app (Mac + Windows) - every prior project was web. Code signing, installer formats, auto-updaters, OS-specific quirks, distribution outside app stores - none of it obvious until you hit it. Also my first time mapping XML from Excel - I came in with browser automation experience and learned the XSD/namespace/tag-hierarchy game from scratch. The rewrite was only possible because the Act II work built the fluency to recognize what the Act III export was telling us.
Vibe Coded with Claude Code:
Full-stack vibe coded - built iteratively through conversation with Claude Code. No spec, no wireframes, no upfront architecture. AI handled the Rust/Tauri shell, Python automation, and JavaScript frontend while I directed the product decisions, tested with real client data, and debugged the edge cases. The two-architecture rewrite story shows what this process looks like in practice - AI writes the code, human catches the architectural smell, AI rewrites the architecture.