Backend & full-stack engineer · Kenya

Software that can't afford to get the money wrong.

I build the backend logic, database design and payment integrations behind systems that move real money. RentManager is mine end to end: a rental platform for Kenyan landlords that collects rent over M-Pesa, keeps an append-only ledger of it, and works out the tax owed under Kenyan law. It's deployed and running on web, mobile and its own API.

Open to work · available now Graduate & junior roles · internships · contract work · remote or on-site

1,588 backend tests · 99 database migrations · 21 modules · no shortcuts on the ledger

01AVAILABLE

I'm open to work

Available now

I'm after my first serious engineering role, ideally somewhere I can keep working on systems where correctness matters. Contract work suits me just as well. Below is exactly what I'm open to, so neither of us wastes an email finding out.

Graduate & junior engineering roles

Backend or full-stack. Java and Spring are my strongest, though I've shipped the TypeScript half of a real product too.

Internships

I'm still a Computer Science student, so an internship fits my schedule. I'd treat it as the real thing.

Freelance & contract projects

SaaS products, business workflow systems, backend and API work, M-Pesa integrations. What that looks like →

Open source & collaboration

I'd like to work alongside engineers with more experience than me. Having someone review my code is worth more to me than pay right now.

Where I can work

Remote, any timezone. I'm on UTC+3 in East Africa, so my working day overlaps Europe, the Middle East, India and most of Africa, with a morning window for the US East Coast. I can also work on-site in Kenya, and I'd relocate for the right role.

How to reach me

WhatsApp is fastest. I check it daily and usually reply the same day. Email is better for anything long.

WhatsApp me Download CV (PDF) LinkedIn
02CASE STUDY

RentManager

Deployed · Live

A property and rental-management platform for the Kenyan market. Landlords manage properties, units and leases. Tenants pay rent by M-Pesa and see a running ledger of every payment. The platform handles commission, payouts and Kenya's monthly rental-income tax rules. I designed and built all of it on my own.

rentmanager-ke.netlify.app ↗

The source is public, so none of the numbers below have to be taken on trust: backend, web, mobile.

RentManager's public landing page: headline 'Find your next home', a property search bar, and sign-in options for landlords and renters.

The live RentManager product. Click through to open it.

It ships on three surfaces

Web

Landlord & tenant app

62 routes covering the landlord dashboard, the tenant portal, public listings and reservations, and an admin console.

Next.js 16 · React 19 · TanStack Query
Mobile

Native app

44 screens with push notifications, offline detection, secure token storage and crash reporting.

React Native · Expo · Expo Router
API

The backend both use

21 modules behind one contract. Tenant isolation comes from the verified token, never from a request header.

Java 21 · Spring Boot 3 · PostgreSQL
2M-Pesa flows (STK + B2C)

Read the full engineering case study →

03SKILLS

What I can do

Every rating here comes from something I've shipped and maintained, with code running in RentManager behind it. Where I'm still shallow, the meter says so.

Built & hardened in production Shipped with it, still deepening Learning, not yet shipped

Backend

Java 21 The language I'm most fluent in. Records, sealed types, the whole backend.
Spring Boot 3 Web, Data JPA, Security, Validation and scheduled jobs, across 21 modules.
API & domain design Ports and adapters, with one ArchUnit rule holding the property module's layering in place. Widening it to the other modules is on my list.
Spring Security · JWT / JWKS Tenant identity comes from a verified token claim, never from a header a client can set.

Data

PostgreSQL Schema design, constraints, indexing, and an append-only ledger you can reverse but never edit.
Flyway migrations 99 sequential migrations, none edited after it ran. The schema's history stays honest.
JPA / Hibernate Comfortable day to day. Still learning when to drop to SQL instead of fighting the mapper.

Frontend & mobile

TypeScript Types generated from the backend's OpenAPI contract, so a breaking change fails at build time.
React 19 · Next.js App Router 62 routes across four different user roles, server and client components.
TanStack Query Cache keys, invalidation after mutations, optimistic updates where they're safe.
React Native · Expo 44 screens with push notifications, secure storage and offline handling. It's shipped, though not yet in an app store.

Payments & integrations

M-Pesa Daraja STK push and B2C payouts, with idempotency windows and callback handling that survives retries.
Clerk (auth) JWKS verification, webhooks, role and organisation mapping across web and mobile.
Africa's Talking SMS Sent through a database-backed outbox, so a failed message retries instead of vanishing.

Testing & delivery

JUnit 5 · Mockito 1,588 backend tests across 261 test classes. The money paths are covered first.
Testcontainers · ArchUnit Real PostgreSQL in integration tests through Testcontainers, with one architecture rule enforced by the suite itself.
Docker · deployment RentManager is containerised and deployed: API on Render, web on Netlify, Postgres on Neon.
Observability at scale Sentry and health checks are wired up. Running a system under real load is the gap I most want to close.
Distributed systems I've avoided this on purpose. A modular monolith was the right call at this size, and I'd rather say so than pretend otherwise.

The longer version, with evidence →

04IN THE CODE

Judgement, in the code itself

Anyone can describe how they think. These are four comments from RentManager's backend, unedited, each linked to the file it lives in. They are the clearest evidence I can give you of how I reason about a system.

StkPushRateLimiter.java view on GitHub ↗
This does NOT replace the DB-level idempotency guard
(uk_rent_transactions_tenant_external_reference) — that's what prevents a
successful payment from ever being double-applied. This only prevents
wasted/duplicate STK prompts from being sent to Safaricom in the first
place, which is a cost/UX concern, not a correctness one.

Knowing which guard actually protects the money, and refusing to let a convenience feature take credit for it, is the difference between a system that looks safe and one that is.

SlidingWindowRateLimiter.java view on GitHub ↗
Replaces SimpleRateLimiter, which could not work.

It kept an int per key with no time component at all, so its limit of 100
was a lifetime cap that latched closed permanently. Its putIfAbsent / get
/ put sequence was a non-atomic read-modify-write, so two concurrent
requests could read the same count and both write count + 1,
undercounting under exactly the load a limiter exists to stop. And its map
never evicted anything.

That is me reviewing my own earlier code and finding a race condition in it. The replacement mutates inside ConcurrentHashMap.compute, so the check and the record are one atomic step.

AuditLogJpaEntity.java view on GitHub ↗
Stands alone rather than extending BaseTenantEntity: the base carries
updated_at and an @Version column, and V86 forbids updates outright.
Inheriting mutability machinery onto an append-only table adds two columns
that can never move and invites someone to try.

Designing so the wrong thing is hard to express, rather than merely forbidden in a document, is the habit I most want to keep.

V103__tenant_profile_landlord_added_renters.sql view on GitHub ↗
Until now the ONLY production code that created a tenant_profile row was
a stranger reserving a vacant unit on the public site and paying a
deposit by M-Pesa. A landlord signing up with tenants already living in
their units had no way to enter them, so no lease, ledger, payment or
renter portal could exist for an existing tenancy. That is the normal
case in Kenya, which made the product unusable for it.

  clerk_user_id NOT NULL — a renter added by their landlord has no
    Clerk account yet, and may never create one (a caretaker collects
    cash).
  email NOT NULL — many renters give only a phone number.

The bug here was not in the code. It was an assumption in the schema: that every renter arrives through the website. Most Kenyan tenancies already exist before the software does, and two NOT NULL constraints quietly made the product useless for them.

Read the backend ↗

05WORK

What I build

Software for businesses with real workflows and real rules to enforce. Not marketing sites, and not prototypes that stop at the demo.

SaaS platforms with real business logic

Multi-tenant systems where the hard part is the domain, not the interface: leases, ledgers, approvals, commissions, and the rules about who may do what to whose money.

Payment & third-party integrations

M-Pesa Daraja (STK push, B2C payouts, callback handling), SMS delivery and identity providers. It's unglamorous plumbing, and it has to be idempotent and auditable rather than merely working on a good day.

Business workflow systems

Turning a process that currently lives in spreadsheets and WhatsApp threads into software with an actual data model and an audit trail behind it.

Full-stack web & mobile applications

Next.js and React on the web, React Native on mobile, Spring Boot and PostgreSQL behind both. I build and own every side of the contract between them.

06APPROACH

How I think about building this kind of system

Five things I've come to hold as rules, from building RentManager's money paths.

PrincipleWhy
Money is append-only.Every rent transaction is written once and never edited or deleted. A mistake gets reversed with a new, linked entry, so the ledger still shows what happened, including the correction.
Don't reach for distributed complexity you don't need yet.RentManager runs as a modular monolith with a database-backed outbox instead of a message broker. A queue would have been a more "impressive" architecture diagram and a worse decision at this scale.
Fail closed on anything legal or financial.The tax module computes VAT and rental-income tax against real Kenyan law, but every gate defaults to off until a human confirms it applies. Wrong-by-default is not an acceptable failure mode for a tax calculation.
The schema is a historical record, not a draft.99 sequential migrations, never edited after they've run. If something needs to change it gets a new migration, so the database's history stays honest.
If it isn't tested, assume it's broken.1,588 backend tests, including an architecture test that fails the build if the property module's application layer reaches into its infrastructure. The number isn't the point. The parts I haven't tested are the parts I don't trust, and I'll tell you which those are.
07PROCESS

How I work

  1. 01
    Understand the problem.

    What's manual today, what has to never go wrong, who actually uses this.

  2. 02
    Map the domain before writing code.

    Entities, rules, edge cases. The expensive mistakes happen in the model, not the syntax.

  3. 03
    Design the architecture, and write down the trade-offs.

    What I chose, what I rejected and why, so a decision can be revisited later instead of argued from scratch.

  4. 04
    Build in focused, tested increments.

    One module and one migration at a time, rather than a big-bang integration at the end.

  5. 05
    Test the money paths first.

    Anything irreversible gets covered before the interface around it does.

  6. 06
    Ship, then say what's still open.

    A production checklist, plus a written list of what isn't hardened yet. I don't claim everything is finished.

08ABOUT

A bit about me

I'm a Computer Science student in Kenya. Most of what I know about building software came from building RentManager end to end, not from a course. I like problems where correctness matters more than speed: money, records, anything a business can't afford to get wrong. I'm building depth by taking on harder versions of that same problem.

More about me →

"I'm building depth through increasingly complex systems, not claiming expertise I haven't earned yet."

Open to work · available now

Hiring, or have something that needs building?

If it involves real business rules, money, or records that have to stay correct, that's the problem I want to work on. Tell me what you're building, or what the role is.