Most tutorials start with routes. I started with the schema — here's why that decision saved the entire project.
Full-Stack Dev · Data Scientist · ML Engineer — building production systems from Kenya
Open for freelance, health-tech & ML collabs.
I build full-stack systems and ML pipelines — from Kenya-specific production software to reinforcement learning agents that play video games.
I think in systems, ship in Python, and occasionally summit mountains.
Full-stack optical clinic management system built for Kenya — SHA insurance, KRA receipts, MCK registration, Africa's Talking SMS, Kiswahili prescription slips.
PythonFastAPIPostgreSQLDockerNginxTelegram AI agent on the OpenAI client library.
ML hackathon — business viability scores for Montgomery, AL.
Reinforcement learning + computer vision agent trained to play Need for Speed: Most Wanted Black Edition.
Thoughts on systems, ML, and shipping software from Nairobi.
Most tutorials start with routes. I started with the schema — here's why that decision saved the entire project.
From foreign key ordering bugs to Nginx proxy configs — a no-BS deployment journal.
Computer vision + reinforcement learning applied to NFS: Most Wanted. Early progress, honest struggles.
Most tutorials tell you to scaffold routes first. I did the opposite — and it's the reason OptIVision didn't collapse under its own weight.
With 12+ interconnected tables, your schema is your architecture. I hit a foreign key ordering bug in init.js where tables were created before their dependencies.
-- Wrong: referencing a table before it exists
CREATE TABLE prescriptions (
patient_id UUID REFERENCES patients(id)
);
-- patients table created AFTER this line
Draw entity relationships before writing any application code. For a Kenya-specific system — SHA codes, KRA PINs, MCK registration numbers all linking correctly — the schema is the product.
Deploying to a VPS sounds simple until you're staring at a 502 Bad Gateway at 1am. Here's the unfiltered version.
Docker Compose doesn't guarantee database tables are created in dependency order. Silent failure that only surfaces on first insert.
location / {
proxy_pass http://app:8000;
# NOT http://localhost:8000
}
Containers communicate by service name inside a Compose network — once you internalize that, everything clicks.
The goal: an RL agent that plays NFS: Most Wanted Black Edition using computer vision for state perception.
import cv2
frame = cv2.cvtColor(raw_frame, cv2.COLOR_BGR2GRAY)
frame = cv2.resize(frame, (84, 84))
Defining a good reward function is harder than the model. I'm reading pixel color values from screen regions as a road/off-road proxy since I don't have game internals access.
Have a project in mind? Let's talk.
Thanks for reaching out. I'll get back to you within 24 hours.