Software Development Engineer
Easley Dunn Productions · Feb 2026 – Present
SafetyNet is a cross-platform React Native social app — a privacy-first replacement for a phone's Contacts app, with relationship-tiered trust, encrypted real-time chat, and live location sharing. I'm the top contributor by commit volume and own the backend, database, and deployment-infrastructure workstreams end to end, on top of shipping mobile features.
Database hardening audit
Ran a full audit of the Postgres layer — every migration, the complete schema, and all ~44 query call sites — against the live database using EXPLAIN and index inspection rather than just reading code. Found and fixed several real production risks: no baseline migration existed at all (a fresh clone got an empty database with no way to bootstrap it), stateless JWTs had no revocation path (a stolen phone or password reset didn't invalidate sessions for up to 90 days), and multi-step writes like signup and message-sending had no transaction wrapping, so a mid-sequence crash could leave orphaned records behind.
Also found check-then-write races: a few endpoints did a SELECT followed by a conditional INSERT against unique constraints, so concurrent requests could 500 instead of returning the existing row. Replaced with atomic INSERT ... ON CONFLICT DO UPDATE — and caught a real bug in my own first draft this way, where the fix was silently clobbering real user data with an insert-only default on every update.
A silent index bypass
The most interesting finding was invisible without EXPLAIN: phone-number lookups ran a REGEXP_REPLACE comparison against every row, which turned out to be a full sequential scan that completely bypassed the existing unique index, because stored and query-time values were normalized differently. Fixed at the root by normalizing phone storage to one canonical format across 6 call sites — and the backfill migration for that fix surfaced a real, pre-existing duplicate-account collision already sitting in production data.
A related case: a table's index was built on generated columns that zero live queries actually used, because every real query filtered on the raw columns instead — a live index that had never once served a query. Rewrote the query shapes to match, confirming every fix with before/after EXPLAIN output rather than assuming.
Dual-environment infrastructure
Designed and built full internal/production isolation across every layer of the stack: two separate Firebase projects (closing a real cross-environment exposure where the lower-trust internal project's database held a pointer to the production backend's URL), two independent backend processes rather than one process routing on a request header, and two PostgreSQL databases seeded from a schema-only dump so production inherits every hardening fix automatically instead of needing each fix applied twice.
Also diagnosed a genuinely obscure native-Android bug during real-device verification: a config library's runtime reflection resolved Android's BuildConfig via the app's applicationId, but the Gradle namespace (where BuildConfig is actually generated) had quietly diverged from applicationId since before the environment split — so configuration silently returned empty on-device with no visible error under the app's own log tag. Fixed with a documented Gradle resValue override and verified end-to-end on a physical device across both build flavors.
Testing and security
Built out 145 integration tests running against a real, non-mocked PostgreSQL instance in CI, because the team had previously been burned by logic that only worked against a fake database — while deliberately mocking the Firebase Admin SDK, since faking auth calls is safe but faking the database isn't. GitHub Actions spins up an isolated Postgres container per run, so no contributor needs local Docker to get a passing or failing signal on a PR.
On security: closed an IDOR on the chat-messages endpoint by adding a participant check, removed client-supplied user-identity trust from the Socket.IO join handler in favor of JWT-only identity, and required server-side Firebase token re-verification on password reset instead of trusting client-asserted OTP success.
A documented trade-off
Pushed back on an early planning-doc proposal to migrate connection lookups to Neo4j "as the network grows." At the actual scale — around 130 connections, 82 users — the real bottleneck was the Postgres indexing mismatches above, not a graph-database problem. Documented the reasoning, fixed the indexing instead, and revisited the case for Neo4j afterward: it got weaker, not stronger.