Written by: Henry Odhiambo — Full Stack Developer
Our app froze for 10+ seconds every time a user ran a search.
Not a little stutter. Full lock-up. The kind where you're not sure if the app crashed or if you should just wait. Users didn't wait.
This is the story of how we built a patent tracking app with an architecture that couldn't handle its own data — and what the rebuild looked like.
The Original Stack
We went with a setup that looked clean on paper:
- Svelte for the frontend (lightweight, reactive, fast)
- Supabase for auth, database, and backend services
- FastAPI (Python) for patent-specific API routes
- Capacitor to wrap it into a cross-platform mobile app
The idea was: build once, ship everywhere. Svelte handles the UI, Supabase handles the plumbing, FastAPI handles the domain logic, Capacitor handles iOS/Android.
Sounded efficient. It wasn't.
Where It Broke Down
Problem 1: Capacitor Wasn't "Write Once, Run Everywhere"
If you've used Capacitor for anything beyond a basic app, you probably know where this is going. iOS and Android behaved differently in ways we didn't anticipate. Platform-specific bugs ate weeks of dev time. Custom logic had to be rewritten for each platform anyway.
The "time savings" of a single codebase evaporated.
Problem 2: Client-Side Processing of Heavy Data
This was the real killer.
Patent documents aren't lightweight. They're massive, deeply nested data structures with dozens of fields — assignees, claims, classifications, citations, legal events, you name it. Our architecture had the client doing all the heavy lifting: fetching raw patent data, parsing it, and rendering thousands of data points in the browser.
On desktop, it was slow. On mobile, it was unusable. Full freezes, sometimes 10+ seconds, while the device struggled to process the data.
We were essentially asking a phone to do the work of a server.
Problem 3: Scraping Was Eating Our Server Alive
Patent data isn't available through a nice clean API. We had to scrape it. FastAPI and Python let us build the scraping pipeline quickly, but Python's memory management under heavy concurrent load wasn't great for our use case. Response times ranged from 2 to 20 seconds depending on load.
High usage periods would exhaust server memory completely.
The Feedback Confirmed What We Already Knew
Early adopters didn't hold back:
- "App constantly freezes"
- "Takes forever to load anything"
- "Crashes when I try to search"
This wasn't a bug we could fix with a patch. The architecture itself was the problem. We had two choices: keep patching a broken foundation, or start over.
We started over.
The Rebuild: What Changed
We kept what worked and replaced what didn't.
Kept:
- Svelte — still lightweight and reactive, and we'd already built up component logic we could reuse
- Capacitor — still useful for cross-platform, once we stopped fighting it and moved heavy logic off the client
Replaced:
- Moved all patent data processing to a dedicated Go backend
- Replaced the "search everything" model with focused tracking and alerting
Why Go?
The Python/FastAPI scraping layer was the main performance bottleneck. We needed something that could handle concurrent scraping jobs without choking on memory, with predictable performance under load.
Go gave us:
- Native concurrency with goroutines (no need for async/await gymnastics)
- Low memory footprint per request
- Predictable, consistent response times
- Easy deployment as a single binary
We didn't need a framework. Go's standard library plus a router handled the API layer. The scraping pipeline runs as a background service with worker pools.
Architecture Before vs After
v1 (broken):
User Device → Supabase → Raw Patent Data → Client-Side Parsing → Render
↕
FastAPI (Python) → Scraping → High Memory Usage
v2 (current):
User Device → Svelte UI → Go API → Pre-Processed Data → Render
↕
Go Workers → Scraping → Cached Results
The key shift: the client now receives pre-processed, summarized data instead of raw patent documents. The Go backend does the parsing, summarization (using AI for plain-English patent summaries), and caching before anything hits the frontend.
The Numbers
| Metric | v1 | v2 |
|---|---|---|
| API response time | 2–20 seconds | 2–4 seconds |
| App freezes | Constant | Rare |
| Speed-related complaints | Most of them | Almost none |
| Memory usage under load | Spiking/crashing | Stable |
What We Learned
1. "Write once, run everywhere" is marketing copy.
Cross-platform tools save some time. They don't save as much as their landing pages promise. Budget for platform-specific work anyway.
2. Don't make the client do server work.
If you're processing anything heavier than form validation, do it server-side. Especially on mobile. This sounds obvious in retrospect, but when you're building fast and everything works on your dev machine with a good connection, it's easy to miss.
3. Match your language to your workload.
Python was great for rapid prototyping. It was not great for concurrent scraping at scale with tight memory constraints. Go was the right tool for this job. Not every job — but this one.
4. Scraping needs its own infrastructure.
We treated scraping as "just another API call." It's not. It's unpredictable, resource-intensive, and fragile. It deserves its own worker pool, its own error handling, its own monitoring.
5. Sometimes you have to throw it all away.
We spent weeks trying to patch v1 before accepting the foundation was broken. The rebuild took less time than the patching would have.
The Product Shift That Came With It
The rebuild also forced a product rethink. v1 tried to be a general patent search engine. v2 focuses on active monitoring:
- Watch-lists for companies, patent categories, and individual patents
- Alerts when something changes (new filings, grants, expirations)
- AI-generated plain-English summaries so you don't have to parse legal language
Narrowing the scope made the engineering simpler and the product more useful. Turns out "do one thing well" applies to architecture and product design equally.
Questions for the Dev Community
For anyone who's done a major rewrite:
- How did you make the call between patching and starting over? Was there a specific moment or metric that tipped it?
- If you've migrated from Python to Go (or another compiled language) for performance reasons — what surprised you about the transition?
And for anyone dealing with scraping at scale:
- What's your stack for it? Worker pools, message queues, something else?
- How do you handle the unpredictability (rate limits, layout changes, downtime)?
Patent Trail is a patent monitoring tool built for people who need to track filings and expirations without enterprise pricing. If you're curious, it's at patentrail.com.
Top comments (0)