TaxEasy
Overview
TaxEasy is a private tax management SaaS for tax agents and businesses. The work centers on documents: invoices, receipts, bank notifications, declarations, and the evidence behind a filing.
The core constraint is the deadline. A failed document is not only a technical error. It is a tax agent who cannot file on time.
Context and constraint
The platform serves people working against filing deadlines. When an invoice or receipt does not arrive, the support problem is urgent and specific. The system has to show where a document is in the pipeline, why it failed, and what to do next.
That changes what "done" means. Reliability, visible job state, and a clear runbook matter as much as feature delivery.
My role and ownership
I worked on TaxEasy as part of The Resonance Team (Technical Lead, Apr 2025 to present) in a team of five, contributing across platform delivery and operations. My ownership included implementation work and the engineering practices that kept the system operable: pipeline behavior, failure handling, build and deploy improvements, and cross-app consistency.
System and capabilities
- Monorepo and apps: 5 apps in a Turborepo monorepo (
api,web,portal,mobile-rn,mobile-flutter). - Document ingest: Email ingest flow via Cloudflare Email Routing → Worker → webhook → NestJS → PostgreSQL + S3. The platform processes XML invoices, SMS bank notifications, and receipt OCR through BullMQ.
- AI and extraction: Vercel AI SDK v7+ LLM gateway with multi-provider abstraction, and an OCR vision pipeline for receipt and invoice extraction with zod-validated structured output.
- Anomaly detection: 8 detectors (including late filing, missing declaration, partner KPI breach, high refund rate, and revenue mismatch), with an auto-close lifecycle and ZNS notifications.
- Operations visibility: Observability with Grafana, Loki, Tempo, Prometheus, and an OpenTelemetry collector so document and job states can be traced.
8 detectors
State + owner
Auto-close / ZNS
Resolved signal
- late filing
- missing declaration
- partner KPI breach
- high refund rate
- revenue mismatch
- + 3 more detectors
open · visible → owned
auto-close + ZNS notify
resolved · measured, not just detected
Technical highlights
- Queues and job states: BullMQ jobs can succeed, fail, retry, or wait for a human. Making that state visible is a key product behavior for support and on-call response.
- Build and delivery: Docker build time reduced from about 10 minutes to about 30 seconds with pnpm cache mounts and multi-stage builds. Self-hosted GitHub Actions runners with Turborepo remote cache; tag-push CD (build → GHCR → VPS over SSH → docker compose); Nginx reverse proxy and SSL termination across 5 domains.
- Cross-platform parity: React Native app brought to pixel parity with a Flutter baseline across 12 money screens, with token parity checked by a Jest diff and behavioral parity by 6 Maestro flows.
- Data layer maintenance: Prisma 7
prisma-clientmigration across 75 files and missing foreign-key indexes added.
Challenges and trade-offs
The hardest trade-off was balancing fast delivery with operational clarity. A detector that flags everything becomes noise for tax agents, and a pipeline without explicit states turns every failure into a long hunt.
We treated lifecycle and visibility as first-class work: detector flags needed owners and resolution paths (auto-close and notifications), and failed documents needed a short runbook path (find the job, inspect state and parser/OCR/schema results, replay or route to a human, and communicate clearly).
Outcome and status
- Status: Private platform.
- Scale signal: 309 merged PRs in the main platform repository (verified via GitHub CLI).
- Result in practice: The platform supports document ingest, extraction, anomaly workflows, and observable operations for tax agents working under deadline pressure.
What I would improve
I would build the failure queue and document lifecycle instrumentation earlier, before expanding detection coverage. I would also invest sooner in support-facing error messages and runbook quality, because those are often the fastest way to reduce the real cost of failure for users.