I design systems for the failure cases, not just the happy path.

Backend heavy full stack software engineer with 7 years of experience building reliable, end-to-end applications.

01

About

I'm Abdulkerim Mohammed, a full-stack engineer with seven years of experience delivering end-to-end systems from responsive TypeScript frontends to high-throughput distributed backends in Go and Python. I bridge the gap between user-facing product requirements and robust backend architecture, knowing when to leverage event-driven microservices and when a clean monolith is the right call. Comfortable owning the complete lifecycle from UI components to production deployments on the cloud, my focus is on building performant, observable systems that scale cleanly.

Abdulkerim Mohammed your photo here
02

Education

I did my undergraduate studies in Computer Science at Addis Ababa University, then stayed on for a Master's degree in Software Engineering at the same university, completing it with a 3.5 GPA and thesis work rated Excellent.

Master's degree certificate master's certificate here click to zoom
03

Skills & stack

Languages & protocols

GoTypeScriptPythongRPCWebSocket

Databases & caching

PostgreSQL Mysql MongoDBRedis

Messaging

RabbitMQKafka

Observability

JaegerPrometheusOpenTelemetry Grafana

Infra & cloud

KubernetesDockerGCPAWS

CI/CD & testing

GitHub ActionsGitLab CIk6Jest
04

Project Case studies

01: distributed system (Microservices)

Addis Ride

A distributed, microservices-based ride-hailing platform built to handle the real-time mechanics of ride matching and telemetry, with isolation and consistency boundaries enforced by design rather than squeezed into a single database.

Problem & context

Ride-hailing platforms look simple on the surface, but running one is a genuinely hard distributed systems problem: driver location updates stream in every few seconds, driver-rider matching has to happen instantly, and trip states, ride offers, and payments must coordinate across separate systems without dropping data or breaking down. Monolithic setups fall apart under this workload because heavy real-time operations—like tracking active drivers, keeping live map locations in memory, and searching for nearby rides—compete for the same database and CPU power as trip records and payment transfers.

Addis Ride was built as a distributed microservices system to solve this by isolating driver tracking, trip management, and payments into independent services. That meant establishing clear ownership boundaries between services, picking different protocols for different jobs (direct gRPC calls for internal service-to-service requests alongside event streams for asynchronous tasks), and making practical trade-offs between instant data syncing and service independence. It also required building in distributed tracing and metrics from day one, because tracking down a bug across multiple services without full request visibility is nearly impossible.

Architecture

Four core services plus supporting infrastructure, each communicating over the protocol suited to its job: REST and WebSocket at the edge, gRPC internally, pub/sub for anything asynchronous.

rider app driver app api-gateway REST + WS trip-service OSRM · fare · status driver-service geohash matching payment-service Stripe checkout redis WS connection registry rabbitmq topic exchange event · cmd jaeger · prometheus tracing + metrics gRPC internal · amber = gateway boundary · teal = async pub/sub
  • API Gateway: the single entry point for both rider and driver clients. Handles REST (for one-off request/response actions like previewing a route or creating a trip) and WebSocket (for real-time, bidirectional push, things like driver trip offers, real-time driver location updates, live trip status updates, payment link delivery). It also acts as the boundary between the outside world and internal services, translating client requests into gRPC calls internally.
  • Trip Service: owns trip creation, route calculation (via OSRM, an open-source routing engine), fare calculation, and trip status transitions (requested → accepted → in-progress → completed).
  • Driver Service: owns driver registration (name, phone, car plate) and driver-to-trip matching logic (finding available drivers).
  • Payment Service: integrates with a payment provider (Stripe) to create checkout sessions and process payments once a trip is complete.
  • Message Broker (RabbitMQ): handles all asynchronous inter-service communication using a Pub/Sub pattern via topic exchanges, so services publish events/commands without knowing who (if anyone) consumes them.
  • Redis: used by API Gateway to store active client WebSocket connection metadata in a shared, external store rather than in-memory, enabling the gateway to scale horizontally across multiple instances while still reliably routing real-time pushes to the correct connected client.
  • Observability (Jaeger, Prometheus): handles distributed tracing using Jaeger for individual request tracing, and Prometheus for overall aggregated system metrics.
Sequence diagram

End-to-end request flow tracing a trip from rider request through driver acceptance to payment—showing how the trip service, driver matching engine, notification service, and payment gateway coordinate across gRPC calls and event streams.

addis-ride sequence diagram
Key technical decisions

Driver–rider matching via geohashing

Drivers push live location continuously over an open WebSocket connection; Driver Service keeps a geohash-encoded index of positions up to date for efficient proximity queries. On trip creation, Trip Service publishes an event, Driver Service queries nearby available drivers and pushes an offer back through API Gateway. Driver Service never talks to a client device directly.

RabbitMQ over Kafka

The messaging need was discrete, one-off business events and commands requiring reliable routing, things like "a trip was created" or "a driver accepted", not high-volume streams needing replay or historical processing. RabbitMQ's exchange-and-queue model with topic-exchange pub/sub routes individual business events to exactly the services that need them, without the operational overhead Kafka's log-based streaming would add for a workload that never needed stream processing.

Redis for WebSocket connection state

API Gateway needed to scale horizontally across multiple instances behind a load balancer. In-memory connection tracking would mean a push could miss its target if the instance handling it wasn't the one holding that user's actual socket. Shared connection metadata in Redis lets any gateway instance resolve where a user is connected and route correctly.

Outcome
  • End-to-end trip lifecycle working: route preview and fare estimation, event-driven matching, real-time updates to both rider and driver over WebSocket, Stripe payment, and completion.
  • Every communication boundary (REST, gRPC, WebSocket, RabbitMQ pub/sub) was a deliberate, justified choice rather than a default.
  • Deployed with Docker and Kubernetes, with Tilt for local development.
  • Demonstrates service decoupling, event-driven design, ports-and-adapters architecture, and distributed tracing/observability in practice.
02: big monolith system

TikPartner

A platform connecting TikTok influencers with brands for paid campaigns, built as a single, well-structured monolith, with trust between both sides enforced structurally through the platform rather than assumed.

Problem & context

Built to solve two problems seen firsthand in Ethiopia's creator economy: creators frequently completed sponsored content and either never got paid or were underpaid, with no real recourse; and brands had no reliable way to find influencers who actually matched their target audience beyond guesswork or personal networks. The goal was a system where trust wasn't just assumed; it was structurally enforced through the platform itself: verified data instead of self-reported stats, funds held in escrow instead of pay-on-promise, and a dispute process instead of no process at all. That meant handling real external data (TikTok's API), asynchronous background processing at scale (stat syncing across potentially thousands of influencer profiles), real-time communication, and financial transaction flows with proper safeguards, not just a listing/matching app bolted on top of a database.

Architecture

A single application, deliberately, but composed of clearly separated internal modules rather than one undifferentiated codebase, with background job processing and external integrations (TikTok, Stripe) as the seams where real complexity lives.

tikpartner: monolith core app accounts · campaigns tiktok integration layer verified stats in-app chat scoped to contract escrow payment system stripe + stripe connect dispute resolution separate review state subscription tiers visibility & matching celery + background workers scheduled stat sync · decoupled from request cycle TikTok API external Stripe external task broker Redis dashed boundary = single deployable app · amber = trust boundary · teal = async path
  • Core Application Backend: owns user accounts (influencer/brand roles), campaign creation, contract lifecycle, deliverable submission and review, and subscription tier management.
  • TikTok API Integration Layer: pulls real-time influencer data (follower growth, engagement rates, related performance metrics) directly from TikTok, so brands are matching against verified data rather than self-reported numbers.
  • Celery + Background Worker System: handles scheduled, automated syncing of influencer stats from TikTok in the background, decoupled from the request/response cycle, so stats stay current without manual refresh or blocking any user-facing action.
  • In-App Chat System: real-time messaging between brands and influencers, scoped to a campaign/contract context rather than open-ended messaging.
  • Escrow-Based Payment System (Stripe): brand funds are held in escrow at contract creation and only released to the influencer once deliverables are submitted and approved.
  • Stripe Connect Onboarding: allows influencers to securely link their own accounts so payouts go directly to them once funds are released from escrow.
  • Dispute Resolution System: a built-in mechanism for either party to flag a contract when deliverables or payment are contested, routing it into a separate review state.
  • Subscription/Premium Tier Logic: gates certain visibility and matching features behind a paid tier for influencers, separate from the core campaign/payment flow.
Sequence diagram

End-to-end campaign lifecycle tracing verified stat syncing, contract creation, escrow lock, deliverable review, payout release, and dispute resolution.

tikpartner sequence diagram
Key technical decisions

Verified data over self-reported data

Rather than letting influencers manually enter follower counts or engagement stats, the platform pulls this directly from TikTok's API. A deliberate trade-off: it adds integration complexity and dependency on an external API's rate limits and data availability, in exchange for actually solving the "brands can't tell who's legitimate" problem, one of the two core reasons the platform exists.

Celery for background syncing over on-demand fetching

Influencer stats aren't fetched live on every profile view; they're synced on a schedule via Celery workers. This decouples TikTok API usage from user traffic, avoids repeatedly hitting rate limits or slowing down page loads with live external calls, and keeps stats reasonably fresh without needing to be real-time-accurate to the second, which the use case doesn't actually require.

Escrow over direct pay

Funds move from brand → escrow at contract start, and escrow → influencer only after deliverable approval, rather than a simpler direct-payment model. More complex to implement correctly, since it requires modeling contract state, deliverable-approval state, and payment-release state as related but distinct things, but it's the core trust mechanism the entire platform is built around. Without it, TikPartner is just a directory, not a protection layer.

Dispute resolution as a first-class state, not an afterthought

Contracts can enter a disputed state distinct from the normal in-progress/completed/paid flow. A deliberate design choice rather than something patched on top, since escrow alone doesn't resolve disagreements about whether a deliverable actually met the agreed terms; it just holds the money until someone decides.

Outcome
  • Full campaign lifecycle working end to end: influencer discovery backed by verified, auto-synced TikTok data, in-app negotiation and communication, contract creation, deliverable submission and review, escrow-protected payment release via Stripe, and a fallback dispute process, plus a subscription layer for influencer visibility.
  • Demonstrates practical experience with background job architecture (Celery), third-party API integration under real-world constraints (rate limits, data freshness trade-offs), and payment systems requiring multi-state financial logic rather than simple charge-and-done flows.
  • Trust and accountability mechanisms designed directly into the platform's data model, not bolted on as a policy layer on top of it.

Open to full stack and backend roles.