Available for remote roles and contracts

Jefferson
Gakuya

Full-stack engineer · TypeScript, Go, React Native

I build the parts of a system that everything else depends on: the domain model, the integrations, and the servers it all runs on. Four years at Granular IT in Nairobi shipping healthcare, hospitality, events and retail systems, from ERP migrations and payment flows to offline-first mobile.

Get in touchDownload CVNairobi, Kenya · UTC+3

Selected work

Client names are left out on purpose. Happy to walk through any of these in detail.

Retail e-commerce client · Granular IT · 2026

Swapping the ERP Without Breaking the App on Top

SAP B1beforeERPNextafterMappingfield mapOrder APIsame shapeMobiledata contract unchanged

A retail client is moving from SAP Business One to ERPNext. Three systems my team maintains read from that ERP: a WooCommerce stock sync, an order and invoice backend that feeds a mobile delivery app, and a tool that audits store prices against the ERP price list.

The real risk was the mobile app. It reads SAP-shaped fields such as DocEntry, DocumentLines and custom dispatch timestamps straight from the order backend. So the rule I set was that the backend’s data contract does not change: ERPNext data gets mapped into the existing shape, and the app never learns the source moved.

I wrote the field mappings, the custom fields ERPNext needs, and the API access spec covering which documents we read and which timestamps we write back. That lets the ERPNext team and ours work in parallel against a fixed contract instead of waiting on each other.

  • ERPNext
  • SAP Business One
  • WooCommerce
  • Supabase
  • Node.js

Healthcare client · Granular IT · 2025–2026

A Clinic Management System From an Empty Repo

I was handed a dental practice’s requirements and an empty repository. Before any UI, I modelled the domain: patients, appointments, clinicians, treatments, inventory and invoices, nine entities in all, with PostgreSQL row-level security so each role only ever sees its own slice of the data.

The modules cover scheduling, clinical records, tooth charting with an interactive 3D dental arch (React Three Fiber, FDI notation), and invoicing that satisfies Kenya’s eTIMS tax requirements.

Along the way I worked through seven separate end-to-end CI failures and set one rule for the team: nothing is “done” until you can say where it was verified, whether local, CI or production. It removed most of our “works on my machine” arguments.

  • React Router v7
  • TypeScript
  • NestJS
  • Supabase
  • Playwright
  • Three.js

Agency infrastructure · Granular IT · ongoing

Running Production on Self-Hosted Docker

InternetCoolify host · HetznerTraefikTLS · routingNestJS APINext.jsMSSQLno public port

Our client apps (NestJS, Next.js and SQL Server) run on Coolify across several Hetzner servers, with Traefik routing traffic and handling TLS for four-plus production domains. When something breaks, it usually lands with me.

A few that did: a host with IPv6 assigned but no working IPv6 route, so every Docker image pull timed out until I forced IPv4 precedence for the Docker daemon. Resources created without an https:// scheme, which quietly produced broken Traefik routers and no certificates. The same domains configured on two servers, so Let’s Encrypt renewals kept failing on one of them.

Reading the access logs also turned up active webshell-scanning campaigns, which led to a hardening pass covering SSH, the Docker socket, secrets and exposed database ports. I wrote the checks down as a diagnostic runbook the rest of the team can use.

  • Docker
  • Coolify
  • Traefik
  • Linux
  • Let’s Encrypt

E-commerce client · Granular IT

Two Payment Providers, One Port

Domain LayerPaymentPortPeachAdapteriPayAdapter

The brief was to take payments through both Peach Payments and iPay without tying business logic to either vendor’s API. I modelled the domain around a PaymentPort interface, so the application layer never knows which provider is running.

Each provider lives in its own adapter behind that port. The domain emits the same PaymentInitiated and PaymentSettled events whichever one handled the money, which gives one audit trail and lets retries stay idempotent at the boundary.

Adding or swapping a provider is now one new adapter and its tests, with no changes to the business rules. I’ve since used the same pattern for M-Pesa STK Push and Paystack.

  • Node.js
  • TypeScript
  • AWS Lambda
  • M-Pesa
  • Paystack

Architecture design

Offline-First Mobile for Field Work

SQLiteprimary storeSync Enginequeue · flush · statusLWW + merge conflict resolutionRemote APIREST · idempotent

Field crews lose signal constantly, so the app has to work fully offline. A local SQLite store is the source of truth for every read and write; the UI never calls the remote API directly.

A background sync engine queues each mutation with a monotonic sequence ID and flushes to the API when the device reconnects. Conflicts resolve last-write-wins per field, with a merge fallback for list fields where last-write-wins would lose data.

The engine publishes its state (syncing, pending or error), which drives a small connectivity banner, so users know where they stand without needing to understand replication.

  • React Native
  • SQLite
  • TypeScript

Microsoft × Premier League “Inside the Game” hackathon · Oct 2026 · team of two

Pulse: Match Intelligence Without Made-Up Facts

Pulse turns football match events into live metrics, key moments and commentary. I own the Go side: a seeded, deterministic 11v11 match generator that regenerates byte-for-byte, the metrics and moment detectors, the HTTP/SSE API and the web UI.

The design rule is “facts first, LLM second”. Go computes every number, and the AI agents may only narrate from a verified fact pack, with a verifier checking each claim before a viewer sees it. A 10-step CI gate guards it all, from gofmt to Bicep builds for Azure Container Apps.

  • Go
  • Server-Sent Events
  • TypeScript
  • Azure Container Apps
  • Bicep
View the repository ↗

Experience

  1. Feb 2022 – Present

    Full-Stack & Infrastructure Developer

    Granular IT (formerly Four Africa), Nairobi

    Software agency building web, mobile and infrastructure for clients in healthcare, hospitality, events, retail and manufacturing.

    • Architected a dental practice management system end to end, from domain model to eTIMS-compliant invoicing.
    • Leading the integration side of a client’s SAP Business One → ERPNext migration across three production systems.
    • Built finance modules for an event-management SaaS: contracts, payment schedules, and client and vendor payments.
    • Shipped reviews and NPS across a QR table-ordering web app and its staff mobile app.
    • Run the agency’s Docker/Coolify infrastructure and mentor junior developers on TypeScript and domain modelling.
  2. Nov 2021 – Feb 2022

    Backend Developer Intern

    Whip Music Africa, Nairobi

    Built and tested REST endpoints with Django REST Framework; tuned queries and added caching.

Toolbox

Languages
TypeScript · Go · JavaScript · SQL
Frontend & mobile
React · Next.js · React Router v7 · React Native · Tailwind CSS
Backend
Node.js · NestJS · REST · GraphQL · Server-Sent Events
Data
PostgreSQL · Supabase (RLS) · SQL Server · SQLite · Redis
Integrations
ERPNext · SAP Business One · WooCommerce · M-Pesa · Paystack · iPay · Twilio
Infrastructure
Docker · Coolify · Traefik · Linux · AWS · Azure Container Apps · GitHub Actions
Quality
Domain-Driven Design · Playwright · Jest · ESLint · Prettier

Contact

Open to remote full-time roles and contract work, especially where mobile apps and back-office systems have to agree on the same data. I’m in Nairobi (UTC+3), which overlaps comfortably with European working hours.