Software engineer building high-performance data infrastructure and distributed systems. I primarily write Rust, with a growing interest in ML inference systems. Currently finishing my MS in Computer Science at Northeastern.
A selection of the personal and course projects I have most enjoyed building. Every number on this page comes from a run I can point to, and where I do not have a number, I say so.
Lift-ride events arrive over HTTP, go onto a queue, and are written into DynamoDB with Redis caching the reads. In front of all of that sits a token-bucket admission controller which samples queue depth every 200 ms and steers with AIMD. Backing off is deliberately far more aggressive than recovering: a severe backlog halves the admitted rate straight away, while recovery only adds ten permits a second. If the broker cannot be reached at all, the controller now holds its current rate, because the original version read silence as an empty queue and opened the gates at exactly the wrong moment.
The controller is also fleet-aware. Each replica heartbeats into a Redis sorted set and divides the admission floor by the number of live replicas it can see, so adding a replica does not multiply the rate the cluster admits in total. Scaling up takes effect immediately, but scaling down needs three consecutive confirmations, otherwise a Redis restart could convince a replica it was running alone and hand it the entire budget.
The load harness needed rebuilding before any of these numbers meant anything. The original was closed-loop, which understates latency under load by a factor of 9.5 on this workload. Rebuilding it open-loop, and running every A/B comparison in both orders so that ordering bias could be ruled out, is what makes the remaining figures worth quoting at all.
p50 ms, the same system measured two ways
These numbers come from two different setups. The original ran on AWS, a Spring server on EC2 with RabbitMQ and DynamoDB behind it, and the one report I kept from that build records 200,000 requests at 4,309 req/s with every response a 200. Everything after the rebuild ran against LocalStack on a single laptop, where LocalStack is the binding constraint in every run, so read those throughput figures as a property of the test rig rather than of the architecture. Peak offered load in that sweep was 4,376 req/s, above which the controller shed hard. A batching result which looked like a 16% gain did not survive swapping the run order, so it is not on this page. The CI workflow is written but has never actually run.
My responsibility was the data layer end to end: the PostgreSQL schema, eight Flyway migrations, and the Docker Compose stack the rest of the team developed against. Clinical events vary in shape depending on their type, so they live in a JSONB column with a GIN index over the payload, and a partial index keeps soft-deleted rows out of the paths every other query uses. Deletes are soft throughout so the audit trail survives, which matters for anything touching a clinical record.
The part which mattered most was not code. Three teams were building against the same records, so I wrote the single document defining what a clinical event actually is: every enum lowercase, every field named exactly, and the required fields listed per type. Blood pressure gets systolic and diastolic, never a generic value field. A named drug is always a medication and never a procedure, even when it is injected. That document gave the LLM extraction layer an exact contract to target, and stopped three teams inventing three different shapes for the same record.
Alongside the data layer I acted as integration reviewer for the team, reviewing 14 pull requests and blocking 11 of them on line-level findings: a CascadeType.ALL which would have cascade-deleted patient data, double-encoded JSONB corrupting every API response, a Float and Double mismatch across two concurrent branches, and contradictory prompt instructions producing non-deterministic extraction output.
This was a team project, so the lines matter. The schema, the migrations, the event spec and the Compose stack are mine. The Python extraction consumer and the architecture diagrams are other people's work, and so were the speech-to-text vendor comparison and the model-selection decision. Nobody measured throughput or latency on this build, so there are no performance numbers to show.
The aim was to compare four convolutional architectures, ResNet-50, ConvNeXt-Tiny, EfficientNet-B3 and MobileNetV3, on the same hardware. The harder problem turned out to be the measurement rather than the comparison. MPS and CUDA queue their work asynchronously, so a timer wrapped around a forward pass measures how long it took to hand the kernels to the GPU rather than how long the GPU spent running them.
The protocol the harness settled on is ten untimed warmup passes to absorb Metal shader compilation and let the allocator settle, then a device synchronize so the clock starts on a quiet device, then a hundred timed passes with a synchronize after each one rather than only at the end. It reports mean, standard deviation, min and max, and derives throughput from the mean instead of counting it separately. None of that is complicated, but getting it wrong is easy and the resulting numbers look perfectly plausible.
This was a three-person course project and the architecture comparison was my track. The harness writes its results into a gitignored directory, so there is nothing committed which can be quoted here. Rerunning it and publishing the full table is the obvious next step.
The point of the exercise was to implement the pipeline directly instead of calling it out of OpenCV, so the thresholding, blur and morphology are all written from scratch. Each frame is thresholded using ISODATA over a 6.25% random pixel sample, which keeps that step cheap enough to run on live video, then segmented into connected regions, and each region is rotated onto its axis of least central moment so that orientation stops mattering.
Regions are then described in one of two ways. The first is seven features chosen to survive rotation and scale: how much of its oriented bounding box the region fills, aspect ratio, the first three Hu moments, compactness and extent. The second flattens the region to a 64 by 64 patch and projects that 4,096-pixel vector onto 20 principal components. Matching happens in those 20 dimensions, which is why a single image is enough to teach it a new object with no training run at all.
There is no benchmark committed for this one. Accuracy figures exist in the old write-up, but they sit in an example output block rather than in a recorded run, so they are not repeated here. There is no timing code in the project either, so there is no honest frame rate to quote. The demo video is the real evidence.