Skip to content
View sahilkalgutkar's full-sized avatar
🎯
Focusing
🎯
Focusing

Block or report sahilkalgutkar

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
sahilkalgutkar/README.md

👋 Hey, I'm Sahil Kalgutkar

Software EngineerBackend & Distributed SystemsCloud-Native Platforms

Typing SVG

LinkedIn Email GitHub

GitHub followers GitHub stars Profile views


🚀 About Me

I'm a Software Engineer with 5+ years designing, building, and operating scalable, cloud-native backend services — from greenfield platform components to migrating legacy systems into resilient microservices. I own features end-to-end: clarifying ambiguous requirements, designing the solution, implementing and testing it, and running it in production, while partnering closely with Product, Compliance, and cross-functional stakeholders.

Currently building the Identity & Trust Platform at Ascensus, where I design data models and APIs that serve as the single system of record for customers, entities, and users across the platform.

Comfortable communicating technical designs and tradeoffs to both technical and non-technical audiences, and mentoring engineers to build depth and ownership.


💼 Experience

Software Engineer — Ascensus                                    Mar 2024 – Present
  → Architecting the Identity & Trust Platform: data models, entitlements,
    and shared compliance-context components across resilient microservices
  → Leading migration of identity & customer-lifecycle logic off legacy systems
  → Java · Spring Boot · Python · REST · gRPC · AWS · Kafka · Kubernetes

Software Engineer Intern, AI/ML — Guidepoint                     Oct 2023 – Jan 2024
  → Built a PyTorch/Hugging Face matching system — +25% match relevance, 90% accuracy
  → Elasticsearch-backed retrieval pipeline for real-time matching
  → PyTorch · CUDA · Transformers · Elasticsearch · Docker · Kubernetes

Software Engineer — Wells Fargo                                  Apr 2021 – Aug 2022
  → Backend services for regulated financial applications in production
  → Refactored legacy components into cloud-native, high-availability services
  → Java · Spring · Python · SQL · AWS · Docker · Kubernetes

Software Engineer, Production Support — Infosys                  Aug 2019 – Mar 2021
  → Diagnosed & resolved production incidents; drove root-cause fixes
  → Java · J2EE · SQL · Shell Scripting · Linux · Control-M · Nagios

🛠️ Tech Stack

Languages Java Python Go C# JavaScript TypeScript

Frameworks & Platforms Spring Boot Django Flask NestJS Node.js React .NET

Data & Messaging PostgreSQL MySQL MongoDB Cassandra DynamoDB Redis Kafka RabbitMQ

Cloud & DevOps AWS Azure GCP Docker Kubernetes Jenkins GitHub Actions Prometheus Grafana

AI / Data Tooling PyTorch Hugging Face Elasticsearch Pandas NumPy


📌 Featured Projects

modelforge — A model-serving platform in Go, with an XGBoost scorer verified against XGBoost itself

CI codecov

My other ML projects call a model. This one is the layer underneath them: the thing that decides which version answers a request, groups concurrent requests into one forward pass, and notices when the inputs stop looking like the training data. I wrote the scorer too — it loads XGBoost's own save_model(*.json) and reproduces its predictions in pure Go, with no cgo, so a model version stays a self-contained artifact you can hash and roll back.

  • The fixtures are generated by XGBoost, and that differential test found a bug I would never have found reading the format: XGBoost compares split thresholds in float32 and writes them to JSON at float32 precision, so a threshold reads back as -0.3775961 while the value that produced it is -0.37759611010551453. In float64 they differ and the row goes left; in float32 they are equal and it goes right. Thresholds are chosen from training values, so rows land exactly on them constantly — one row in 64 — and the symptom is predictions correct for 99% of traffic and quietly wrong for the rest
  • base_score turns out to live in two different spaces: single-output models store it in prediction space, so the intercept is the objective's transform run backwards, while multi-class stores a margin-space vector. The binary fixture uses a non-default base_score on purpose, because logit(0.5) is 0 and would make an intercept applied in the wrong space look correct
  • Dynamic batching amortises per-call overhead: 59.5µs/op at batch size 1 against 1.38µs/op at 64, a 43x difference with 320 concurrent callers. The window opens when the first request of a batch arrives rather than on a fixed tick, which is what makes the added latency a genuine ceiling instead of a property of the clock
  • Canary assignment is a deterministic hash of an entity key, so a user cannot flip between control and candidate between requests — otherwise every per-user metric mixes both models and the experiment measures nothing. The model name is mixed in too, so experiments on different models stay independent
  • The rollout guard zeroes a failing version's weight rather than deleting the route, so an operator can see it was pulled — and it will never remove the last serving version, because turning "this version is failing" into "this model serves nothing" is strictly worse
  • Drift is PSI over quantile bins on a window of counts, not retained samples, so watching a service does not get more expensive as it gets busier. Writing the empty-bin test found a second real bug: a quantile edge at the minimum value creates a bin nothing can fall into, which is the ordinary shape for any feature that is zero for most rows
  • Every command and number in the README was run against a live server before being written down, including a known limitations section — 90.5% coverage, and the suite fails rather than skips when Postgres is missing
  • Go · XGBoost · Model Serving · Canary Deployments · Shadow Traffic · Drift Detection (PSI) · Postgres · Prometheus

queryforge — A columnar SQL query engine written from the wire up in Rust

CI codecov

I wanted to know what actually happens between typing a query and getting rows back, so I wrote every stage of it: lexer, parser, binder, cost-based optimiser, vectorised execution engine, and my own columnar file format underneath. No sqlparser, no DataFusion, no Arrow, no Parquet — the whole workspace builds against the standard library alone, because an engine that wraps someone else's parser and someone else's format has skipped the two places the interesting decisions live.

  • The .qfc format puts a footer at the end holding each column chunk's byte range and its zone map, so a reader learns the whole layout in two seeks — which is what turns predicate pushdown from a plan rewrite into skipped I/O: 155x faster on a selective range over 500k rows, reading 3 row groups out of 62
  • Every chunk picks its own encoding by measuring the data rather than guessing from its type, because the right answer changes between row groups of the same column — RLE comes out 10x smaller than plain on a sorted column, a dictionary 4x smaller on a shuffled four-value string column
  • Constant folding deliberately stops at division by zero and integer overflow: folding those would let the plan-time answer differ from the run-time one, which is worse than not folding at all
  • A predicate is never pushed into the padded side of an outer join — doing so deletes rows that should have come back NULL-padded — and join reordering prefers a relation that has a join key over a smaller unrelated one, because an accidental cross product is not something a later choice recovers from
  • The benchmark runs every query twice, on the bound plan and the optimised one, and fails outright if the two disagree on the row count; a flattering speedup from a wrong answer is worse than no number
  • Sorting spills sorted runs to disk and merges them when the input outgrows memory, with a test asserting both paths return byte-identical output — the answer must not depend on whether the data fitted
  • 542 tests at 97% line coverage, and the README records the three bugs I actually hit, including a LEFT JOIN that dropped rows because its ON condition was applied after deciding what matched
  • Rust · SQL · Columnar Storage · Query Optimisation · Vectorised Execution · Zone Maps · Zero Dependencies

tenant-operator — A Kubernetes operator that provisions and continuously reconciles per-tenant namespaces

CI codecov

Every other project here deploys onto Kubernetes. This one extends it: a custom resource, a reconcile loop, admission webhooks and a finalizer, so that "create a tenant" becomes something the cluster API itself understands — eleven lines of YAML become a namespace, a tier-sized quota, a default-deny network policy, generated credentials, a Deployment and a Service, all kept that way.

  • The loop takes its finalizer before it creates anything, and provisions the quota and network policy before the workload — otherwise a crash orphans the namespace, or a tenant briefly runs unquota'd and reachable from every other namespace
  • Deletion blocks until the namespace is genuinely gone; owner references would delete the same objects, but asynchronously, so the Tenant would vanish from the API while its namespace was still terminating
  • It refuses to adopt anything it did not create — and inherits that rule on teardown, so a tenant pointed at another team's namespace will never delete it
  • Admission rejects a :latest image outright: a mutable tag means the spec no longer describes what is running, and no amount of reconciliation can detect that drift
  • The control loop is tested against a real kube-apiserver via envtest — provisioning, drift correction, tier upgrades, suspension, teardown — because a fake client accepts objects a real API server rejects
  • Go · controller-runtime · CRDs · Admission Webhooks · Finalizers · Kustomize · cert-manager · envtest

trust-platform — Multi-tenant identity, entitlements, and a tamper-evident audit log

CI codecov

I build identity and entitlements infrastructure for a living, so I wrote a version of it I can actually show: an OpenID Connect provider, a Zanzibar-style authorization service, and a hash-chained audit log — implemented from the protocol up, not by configuring Spring Security's OAuth support or an authorization SDK.

  • Refresh tokens rotate and carry a family id: presenting one that has already been rotated revokes the whole lineage, because the provider cannot tell the thief from the victim and shouldn't guess
  • Permissions aren't stored, they're derived — a namespace declares that viewers are editors plus whoever can view the parent folder, and a check walks that at query time, so re-parenting a document changes its access with no write against the document
  • Multi-tenancy is enforced in the generated SQL via Hibernate's tenant discriminator, and the isolation suite gives both tenants the same client id and user email so a missing predicate returns the wrong row instead of nothing
  • The audit chain catches four distinct kinds of tampering, including editing only an indexed column — verified by UPDATE-ing a real Postgres table and asserting the verifier names the exact row
  • 431 tests (92% line / 86% branch); the adversarial half covers alg:none, HS256 key-confusion forgery, PKCE downgrade, confused-deputy code redemption, and cross-tenant replay
  • Java 21 · Spring Boot · OAuth 2.0 / OIDC · PostgreSQL · Redis · Kafka · Testcontainers · Docker Compose

raftlite — Raft consensus implemented from scratch in Go

CI codecov

I built the Raft consensus algorithm from the paper — elections, log replication, snapshots, dynamic membership — and put a replicated key-value store on top, with no third-party dependencies: the wire codec, the write-ahead log and the metrics registry are all hand-written.

  • The algorithm is a pure state machine with no sockets, files or clock, so elections, log repair and snapshot installation are tested deterministically instead of by racing real processes
  • Pre-vote and a leader lease, with paired tests running the same partition both ways: with them an isolated node's term never moves, without them it climbs without bound and disrupts a healthy cluster on return
  • A chaos suite runs real nodes against real directories through random crashes, restarts and partitions, asserting one invariant — around 5,000 acknowledged writes across 40 rounds of failures, none lost
  • Two bugs only real sockets could find: a message field threaded through the algorithm but never encoded, and a lost snapshot stranding a follower forever — both fixed with tests that fail without the fix
  • Go · Raft · Distributed Systems · Custom Binary Protocol · Prometheus · Docker Compose · GitHub Actions

PipelineOps — Polyglot job-monitoring & alerting platform

CI codecov

I built a dead-man's-switch monitoring platform for scheduled/batch jobs, across three services I chose for what each does best: a React/TypeScript dashboard, a Django REST Framework API, and a Go/Gin heartbeat-ingestion service.

  • Concurrent heartbeat ingestion in Go with context timeouts, structured logging, and Prometheus metrics
  • Replaced localStorage token auth with httpOnly session cookies + CSRF protection, closing an XSS token-theft vector
  • CI across all three services (lint + tests on every push), Docker Compose locally, Kubernetes manifests for EKS
  • React · TypeScript · Django · Go · Gin · PostgreSQL · Redis · Celery · Docker · Kubernetes

kvforge — In-memory key-value store engine, built from scratch in Rust

CI codecov

I built a Redis-shaped storage engine — RESP-inspired wire protocol, TTLs, an append-only log for crash durability — on nothing but the standard library and tokio, to understand a key-value store from the network layer down rather than wrap an existing one.

  • The AOF durability format reuses the wire protocol itself: each logged write is the same bytes a client would send, so the streaming decoder doubles as the replay parser and a crash mid-write naturally stops replay at the last whole command
  • Async tokio TCP server handling concurrent connections against one shared store; verified end-to-end by running a real server, writing over a real socket, killing it, and confirming a second server replays the data back
  • kvforge-cli (REPL + one-shot modes) routes every command through the exact same parser the server uses, so client and server can't drift apart on what a command means
  • Rust · Tokio · Async I/O · Custom Binary Protocol · GitHub Actions

SplitEasy — Expense-splitting app with automated settle-up

CI codecov

I built a group expense-splitting app (NestJS/Prisma API, React/TypeScript frontend) supporting recurring expenses, invites, and multi-type splits (equal, exact, percentage).

  • Minimum-cash-flow settle-up algorithm built from scratch with a binary max-heap
  • Refresh-token auth: short-lived JWT in memory + hashed refresh token in an httpOnly cookie
  • End-to-end test suite (Supertest against real Postgres in CI) plus unit tests across every service
  • React · TypeScript · NestJS · Prisma · PostgreSQL · Jest · Vitest · GitHub Actions · Docker

Portfolio — Personal site backed by a self-hosted GraphQL API

CI codecov

I built this site itself on Next.js (App Router), serving project data through an Apollo Server route handler backed by Supabase, with a seed-data fallback so it runs fully offline.

  • Next.js · Apollo Server · Supabase · Tailwind CSS · GitHub Actions · Vercel

DigestBot — RAG chatbot over a rolling window of RSS/changelog feeds

CI codecov

I built a retrieval-augmented chatbot that answers questions over live RSS/changelog feeds instead of a static corpus, to explore the parts of RAG most tutorials skip: freshness, dedup, and incremental indexing.

  • Hybrid retrieval blending vector similarity with a recency-decay weight, so answers favor fresh articles without ignoring older ones
  • Forced citations on every generated answer, checked against a hand-built evaluation set to catch regressions
  • Incremental ingestion pipeline: poll → dedup by GUID/URL → chunk → embed → index, run continuously against a rolling feed window
  • Python · RAG · Vector Search · Anthropic · pytest · GitHub Actions

Risk Signal Platform — Event-driven transaction risk-scoring platform

CI codecov

I built three Spring Boot services (transaction-api, risk-scoring-service, alert-service) that talk to each other only through Kafka, each owning its own MySQL database, with a full observability stack (Prometheus/Grafana, ELK) actually wired up and working.

  • Kafka-native retry/DLT for failed alert dispatch via @RetryableTopic, verified by forcing a failure and asserting it lands on the dead-letter topic
  • A real custom Micrometer business metric (risk_scores_total) driving the main Grafana dashboard panel, not just generic JVM stats
  • Idempotent, durable-write-then-best-effort-publish event handling across all three services, backed by Testcontainers integration tests
  • Java · Spring Boot · Kafka · MySQL · Flyway · Prometheus · Grafana · Elasticsearch · Kibana · Docker · Kubernetes

Order Processing Platform — Event-driven order processing on Go

CI codecov

I built a REST API (order-service) that persists to Postgres and publishes to SNS, fanning out over independent SQS queues to two consumers — one writing reservations to MongoDB, one logging simulated notifications — neither aware the other exists.

  • Handler tests run against interfaces the code defines itself (OrderStore/EventPublisher), not concrete Postgres/SNS types — zero network calls, including a test asserting a publish failure still returns 201
  • Raw SNS→SQS delivery and at-least-once handling: consumers only delete their SQS message after the write succeeds
  • Terraform for the AWS ECS Fargate path (reusable service module + least-privilege IAM); Docker Compose with LocalStack for local dev
  • Go · PostgreSQL · MongoDB · AWS SNS · AWS SQS · Terraform · Docker Compose · Prometheus · GitHub Actions

gRPC Catalog Platform — Two Go services over gRPC and REST from one implementation

CI codecov

I built catalog-service to serve one ProductService implementation over both native gRPC and REST (via an in-process grpc-gateway mount, no self-loopback network hop), calling pricing-service's internal-only PricingService over gRPC for quantity-based pricing.

  • Both services share generated code from one buf-managed proto module so the wire contract can't drift between them
  • Request-ID propagation across the gRPC boundary so both services' logs correlate for one end-to-end request
  • Tests run the gRPC server on an in-memory bufconn listener, including a fake PricingServiceServer double for testing the cross-service call without a real network
  • Go · gRPC · Protocol Buffers · grpc-gateway · buf · Docker Compose · GitHub Actions

Ledger Strangler Platform — Legacy monolith strangled into microservices behind a YARP facade

CI codecov

I built a legacy core-banking monolith and strangled it into microservices one route at a time behind a YARP facade — AccountsService (Cassandra-backed) is already peeled off, Statements deliberately isn't yet, and I left that gap honest instead of faking a seamless migration.

  • Balance updates go through a Cassandra lightweight transaction with jittered backoff, verified under real concurrent writers, not just the happy path
  • NotificationsService reacts to a RabbitMQ event the legacy code was never able to produce — the first behavior that only exists because of the migration
  • Serilog → Filebeat → Logstash → Elasticsearch → Kibana across all four services; Terraform for AKS/ACR, ArgoCD watching the manifests directly for GitOps deploys
  • Shipped with a real git-flow history — feature branches merged via PR, a tagged release, and two hotfixes for issues found while testing and in CI, not one commit on main
  • C# · .NET Core · YARP · PostgreSQL · Cassandra · RabbitMQ · Serilog · ELK Stack · Terraform · Kubernetes · ArgoCD · GitHub Actions

advisor-match-service — AI-based client-advisor matching API

CI codecov

I built this to work back through an AI-based matching system the way I first built one during an internship, this time with real infrastructure behind it: a pandas/numpy cleaning pipeline, PyTorch/Hugging Face sentence embeddings, and hybrid Elasticsearch search (kNN blended with BM25) behind a Flask API.

  • A hand-labeled eval harness measures real relevance against the current index — 90% hit@5, MRR 0.90 on the seed dataset, not an assumed number
  • Caught a genuine race condition smoke-testing the real docker-compose stack: two Gunicorn workers both trying to create the Elasticsearch index on boot
  • Terraform for GCP Cloud Run + Artifact Registry
  • Python · Flask · PyTorch · Hugging Face Transformers · pandas · NumPy · Elasticsearch · Docker · Terraform · GitHub Actions

🎓 Certifications & Education

  • AWS Certified Solutions Architect – Associate
  • Certified Kubernetes Application Developer (CKAD)
  • Oracle Certified Professional, Java SE Programmer

M.S. Computer Science — University of Massachusetts, Boston (2022 – 2024) B.E. Computer Engineering — University of Mumbai (2017 – 2021)


📊 GitHub Stats & Activity

Sahil's GitHub stats Sahil's GitHub streak stats
Top languages
Sahil's contribution activity graph
Contribution snake animation

📫 Reach me at sahilkal717@gmail.com or on LinkedIn

Pinned Loading

  1. digest-bot digest-bot Public

    RAG chatbot over a rolling window of RSS/changelog feeds — hybrid recency-aware retrieval, forced citations, and a hand-built eval set

    Python

  2. expense-splitter expense-splitter Public

    TypeScript

  3. kvforge kvforge Public

    In-memory key-value store engine in Rust with a Redis-inspired wire protocol, AOF persistence, and a CLI client

    Rust

  4. PipelineOps PipelineOps Public

    TypeScript

  5. raftlite raftlite Public

    Raft consensus implemented from scratch in Go — leader election, pre-vote, log replication, snapshots, and dynamic membership behind a replicated KV store with an HTTP API and cluster CLI

    Go

  6. risk-signal-platform risk-signal-platform Public

    Event-driven transaction risk-scoring platform on Java, Spring Boot, and Kafka

    Java