A project by Dylan
BridgeSphere
Real estate networking platform MVP, contracted to architect whole platform and infrastructure. Primary full-stack engineer from graduate hire to 1.0 launch. (All visuals contain demo data)

Role
Primary full-stack engineer
Released
Stack
- React
- TypeScript
- ASP.NET Core 8
- PostgreSQL
- Mapbox
- Azure
See it








The Scenario
A product owner approached me after his real-estate networking project had stalled. The previous developer had left five weeks in with only some wireframes and a few technical documents completed, I came on as the sole engineer on a contract basis. I spent over one year building the application from the ground up: architecture, every line of production code, the roadmap, and day-to-day iteration directly with the founder.
The Role
As a recent grad, this was the deep end: I was responsible for the whole platform.
- Architecture design and system diagramming
- Frontend (React SPA) and backend (ASP.NET Core API)
- Database schema, indexing, geospatial queries, and vector search
- Matching pipeline design and optimization
- Authentication, security, and role-based access control
- Infrastructure, CI/CD, and production operations
- Requirements gathering, sprint planning, and client demos
- Direct collaboration with the founder on product direction
What I Built
A monorepo: a React SPA on Azure Static Web Apps, an ASP.NET Core 8 API on App Service, PostgreSQL with PostGIS and pgvector, Mapbox for maps, a FastAPI embedding sidecar, and an Azure Functions image pipeline. Environments promote through Dev, QA, and Prod.

React SPA → App Service API → PostgreSQL/PostGIS/pgvector, with Mapbox, SendGrid, a FastAPI embedding service, and an Azure Functions image pipeline as satellite services, all shipped through a Dev → QA → Prod pipeline.
Matching
Problem:
Early match requests recomputed too much per call: geo candidates, scoring, and embedding similarity. All at once. Under realistic profile volumes, lists felt slow, and repeated dashboard loads made it worse. A full recompute on every request was the wrong default.

Constraints
Data freshness mattered. Agents, Clients, and Brokerages may change service zones and preferences often enough that a stale list will harm trust and user experience. Caching the data without a clearly defined invalidation story was showing incorrect matches, based on stale data. Prior to implementing PostGIS, there was a N+1 problem in the code, where all eligble matches within a large radius were called at once. Then scorers and rank/cache would run across this massive data set. I used the haversine formula for all matches within 300km of the primary location of the user, these bugs were causing fetch requests to take up to 20 seconds to match with 50+ paginated agents.
Approach
I treated matching as a pipeline with an expensive tail, and so it was best to optimize the tail-end of the operation:
- Ran geo filtering early by moving calculations from C# level to PostGIS in PostgreSQL. This caused later stages to work on a much smaller set of data.
- Ran scoring and semantic similarity only on that filtered set.
- Cached ranked lists in memory, keyed by requester and match direction.
- Invalidated the cache on profile and location writes, so edits surfaced on the next meaningful read.
- Added stricter rate limiting on match endpoints in production so retries and abuse couldn’t amplify cost.
The embedding service ran as a separate FastAPI sidecar rather than inside the .NET API. That kept the main service focused, but it also meant cold starts and timeouts were real failure modes I had to design around in the match path.
Result
Match lists became usable for everyday dashboard traffic, and production held up under normal load. Latency is still an honest constraint at scale: better async refresh and smarter caching are the next levers if the user base grows.
What I’d do next
Move more of the heavy recompute off the request path entirely (queue or background refresh), tighten cache keys around the specific fields that actually change scores, and add load testing (k6) as a release gate on match endpoints.
Hard Parts
-
Multi-role surface. One codebase for clients, agents, and brokerages. Shared primitives (auth, maps, cards, save/unsave) had to stay consistent while each role’s path felt complete.
-
Mapping was a core feature, not simply decoration. This required a great deal of reading the MapBox documentation. Agents/Brokerages draw service zones; clients mark preferred areas. Geography actively drives who shows up in a match list, not just where a pin sits on a map.
-
API iteration and maintainability during requirement evolution. I chose to use the “Strangler Fig Pattern” to slowly migrate toward a cleaner v3 API surface while keeping v2 write paths alive, so production never broke mid-migration.
-
Production ownership. Dev → QA → Prod promotion, DNS, cookie-based auth, rate limits, CORS, and API cold-start issues. Operations was critical part of the engineering job, and was never treated like an afterthought. All code was subject to a strict automated testing and promotion pipeline, using Bash Shell scripting.
Tradeoffs
| Decision | Tradeoff | Justification |
|---|---|---|
| Hybrid matching + cache | Latency vs data freshness | Full recompute on every request was expensive and slow |
| Dual v2/v3 APIs | Complexity vs Continuity | I determined it wasn’t feasible to re-write the entire API at once. |
| Separate embedding sidecar | Extra ops | Kept the .NET API focused and RESTful. |
Reflection
Besides having to learn on the go every day, the biggest lesson I took was that production itself is a feature. Knowing what not to expose in the UI, secrets management, branch promotion, DNS, and ops were just as critical as map demos and matching algorithms.
I went from limited professional experience to owning and engineering a full-stack, multi-environment system end-to-end. This includes all the unglamorous work that separates a basic demo from a real, bonafide product.