Wait for the map to settle.
A debounced viewport listener reduces request bursts. Bounding-box queries limit discovery to the visible area, while coordinate and status indexes support filtering.
I built the mobile apps, backend, and organizer dashboard—and the testing, delivery, and observability systems needed to keep them moving together.
A map makes that question intuitive. Behind it, every pan and zoom can trigger a network request, a database query, and another expensive marker render. I worked across the client and backend to keep that interaction lightweight.
I tied discovery to the visible map bounds, debounced movement, and reused marker bitmaps. Each layer avoids work the user doesn’t need.
01 Wait for movement to settle
02 Query the visible bounds
03 Reuse cached marker images
A debounced viewport listener reduces request bursts. Bounding-box queries limit discovery to the visible area, while coordinate and status indexes support filtering.
In-flight Promise deduplication lets identical lookups share one pending result. Redis TTL caching reuses results across requests instead of repeatedly rebuilding them.
Custom Flutter widgets become map-marker bitmaps. An in-memory bitmap cache avoids repeating that CPU-intensive conversion on each render cycle.
An in-flight Promise is shared within a process; it is not a distributed lock across every server replica. Cache lifetimes balance freshness and query load, and coordinate indexes still need validation against the actual query plan.
Flutter discovery, organizer workflows, and real-time conversations share API contracts, authentication, and persisted state. Heavy work moves into dedicated queues.
Better Auth provides organization membership and role-based access. Web sessions use HTTP-only cookies; Flutter persists its cookie jar, with a backend Bearer fallback.
Separate email, notification, data-processing, report, and maintenance queues have distinct concurrency limits, keeping slow work from consuming the same worker capacity.
Event creation saves the event and queues follow-up work. Push notifications and email delivery run asynchronously with retries instead of holding the request open.
Sending a message is easy. Delivering it to the right room, preserving it, tracking reads, and handling unsafe content requires a coordinated workflow.
Authenticated Socket.IO connections join event or direct-message rooms. Persisted messages, read receipts, typing indicators, and offline unread updates keep the experience consistent across sessions.
The moderation path classifies content and masks flagged text before broadcast. Schema-constrained OpenAI responses support predictable handling, while adding an external dependency and latency to that path.
The engineering goal: isolate conversations, preserve state, and make the moderation decision explicit.
Manual release steps and inconsistent test environments make delivery fragile. I built repeatable backend and Android pipelines that turn each change into test evidence, a versioned artifact, and a controlled release.
Real PostgreSQL and Redis services make integration checks meaningful. A consistent Flutter image makes visual tests trustworthy. Versioned artifacts and automated delivery reduce the manual steps between a change and its release.
I used Docker-in-Docker to provision PostgreSQL and Redis, apply migrations, and exercise API behavior against real persistence. Unit, integration, and configured Gatling checks provide different kinds of evidence.
BuildKit reuses unchanged layers; the resulting image is pulled and deployed through Docker Compose. Tests and repeatable packaging reduce release uncertainty.
Feature branches produce QA APKs. Dev builds go to Play internal testing; release tags trigger production delivery through Fastlane. Main builds a production bundle without automatically publishing it.
Signing credentials are injected in CI, while version metadata and generated release notes travel with the build. This replaces a sequence of manual signing, upload, and documentation steps.
Flutter golden tests passed on macOS and failed on Linux CI. Font rendering, antialiasing, and missing fonts made a visual regression check report differences that were caused by the environment.
Exaggerated illustration of rendering differences, not actual test screenshots.
I containerized reference generation using the same Linux Flutter image as CI. Developers regenerate golden images through a Docker script, so baseline and test execution share the rendering environment.
Persistent Pub and Dart-tool volumes reuse dependencies while keeping the workflow compatible with local development tools.
A reliable test must distinguish a product regression from an environment difference. Matching the runtime makes golden failures more actionable; updating a baseline still requires human review of the intended UI change.
I connected test results, deployment metadata, runtime logs, and traces so a failure could be investigated in context.
JUnit results, coverage, integration tests, and Gatling reports converge in a GitLab Pages quality portal.
Commit SHA, message, and branch are injected into load-test report headers, connecting a result to the code that produced it.
Staging container logs become browsable HTML reports. Scheduled pipelines compile 24-hour NDJSON span reports for analysis.
OpenTelemetry and Prisma instrumentation connect request handling to database work. Pino structured logs redact sensitive fields; Grafana/Tempo makes spans inspectable.
Gatling exercises a user flow rather than just an isolated endpoint. The 200-user scenario and zero-failed-request assertion are test configuration—not a claim that every real-world load will pass.
I led two engineering interns across onboarding, analytics, and testing, breaking product work into tasks with clear interfaces and review expectations.
Shared API contracts and validation schemas made the boundaries explicit. Branch conventions, code reviews, and reproducible integration runners gave contributors a repeatable way to validate their work.
The same engineering principle runs through the product and the delivery tooling: make the next step understandable, observable, and repeatable.
A smooth map, a responsive API, a trustworthy test, and a traceable release are parts of the same experience. Popin brought those parts together.
← Back to selected workExplore LetzGo’s route engine ↗