Bitácora Digital
Rescuing and Modernizing a Legacy GovTech Platform
A full architectural overhaul of a production construction supervision platform: from a monolithic, brittle V1 to a multi-tenant SaaS with offline-first sync, immutable audit trails, and asynchronous document generation.
37,100+
Photographic reports in DB
~1,500
Organic users (Firebase Auth)
551
Meeting minutes
2 months
Backend + frontend refactor
The problem
`A system that worked — until it didn't`
Bitácora Digital is used by construction supervisors, municipal inspectors, and private firms to document site visits, checklists, and compliance records in real time — often in locations with no internet. The V1 system had been in production for years and accumulated real usage, but three structural problems had become impossible to ignore.
Sync failures & data loss
The sync mechanism sent a single monolithic JSON payload with Base64-encoded photos, often exceeding 20MB. On unstable connections, these payloads timed out mid-transfer. The mobile client had no way to detect partial success, so it either assumed everything went through or retried everything — causing duplicate records and silent data loss.
Broken multi-user collaboration
To collaborate on a project across organizations, users needed separate credentials for each context. Roles were hardcoded as integer flags. Adding a supervisor to a project from a different company required workarounds rather than a first-class permission model.
Zero extensibility
Every time a new municipality or professional association needed onboarding, the codebase required direct modification. There was no concept of organizations as first-class entities, no tenant isolation, and no mechanism to manage or sell checklist templates across clients.
V1 vs V2
Before — V1
- Users own resources directly
- Roles as integer columns (id_tipo)
- Single-owner project model
- Monolithic sync (Base64 + JSON)
- Mutable checklists (no audit trail)
- Synchronous PDF generation
- Social auth via empty passwords
- AngularJS frontend
After — V2
- Organization-owned data (multi-tenant)
- Spatie roles + project-level permission sets
- Pivot-based multi-stakeholder collaboration
- Decoupled async sync (UUIDs + state machine)
- Immutable versioning with full lineage
- Queue-based PDF with signed URL delivery
- Firebase Admin SDK identity proxy
- Modern SPA (staging)
Architecture decisions
Each decision is documented as an ADR in the public repository.
Organization-based multitenancy
ArchitectureIntroduced an Organization model as the root owner of all data. Laravel Global Scopes enforce tenant isolation transparently. Users belong to multiple organizations via a pivot, solving the multi-credential problem without migrating user accounts.
Immutable versioning & marketplace
Data integrityChecklists are legal documents. V2 never updates rows — every change creates a new version. A root_uuid groups lineage. valid_from/valid_until determine the authoritative version at any point in time. Master templates can be cloned into a buyer's organization as a marketplace primitive.
Contextual roles & permissions
ArchitectureOrg-level roles handled by Spatie, hydrated by middleware. Project-level duties live in a pivot table as permission sets — decoupled from org hierarchy. Admins can assign granular overrides per user without schema changes.
Async sync protocol with state machine
ResilienceReplaced 20MB monolithic payload with a "puzzle of UUIDs": binaries upload separately, metadata syncs as UUID references (~95% smaller). Three-state machine (ORPHAN → PENDING_BINARY → AVAILABLE) handles non-sequential arrivals. SyncSessions with manifests and checksums give granular recovery.
Late binding for race conditions
ResilienceLaravel Batch jobs run in parallel — child records may arrive before parent is persisted. Child jobs release back to queue with backoff until parent exists. Circuit-breaking after max retries logs a "Missing Dependency" error without corrupting state.
Async PDF generation
ArchitectureComplex reports caused timeouts in V1. V2 dispatches GenerateReportJob to background queue. Frontend polls UUID for status and receives a temporary signed URL on completion.
Firebase as identity proxy
SecurityV1 used empty passwords for social auth. V2 uses Firebase Admin SDK to verify idTokens server-side. Social accounts have null passwords — native login attempts are blocked. Multi-device support via user_devices table.
Outcomes
Forensic-grade audit trail
Immutable checklists with full lineage tracking mean historical reports can never be retroactively altered — a hard requirement for government compliance use cases.
True offline-first sync
Sync failures no longer cause data loss or duplicates. Inspectors on construction sites with poor connectivity get granular confirmation of exactly which records synced and which need retry.
SaaS-ready multitenancy
Onboarding a new municipality or professional association no longer requires code changes — it's a data operation.
Parallel delivery strategy
V1 remained in production throughout the entire refactor. V2 was developed in a separate staging environment, keeping ~1,500 active users unaffected.
`The hardest part wasn't the technology — it was making decisions with incomplete information while keeping a live system running. Every ADR was a forced conversation with future-me about tradeoffs I couldn't undo.`
V2 currently in staging. Mobile refactor in progress. V3 roadmap includes migration to private storage with signed URLs (ADR 0008).