Selected Case Studies

National Education Management Information System (EMIS)

Ministry of Education & Sports, Uganda

Backend / Full-Stack Engineer — SMS ONE (U) Limited

Situation

Uganda's education sector ran on data that could not be trusted. National identity records and learner records lived in separate systems with no verification at the point of entry, so the school census was only as good as the weakest handoff.

The reporting problem sat on top of the data problem. Staffing, enrolment and district performance were held in different places and reconciled by hand, which meant the figures behind budget and policy decisions were late and weakly grounded.

Task

Build the learner and workforce backbone of the national platform: a learners module that registers every child in the country against a verified identity, an HR module covering the teaching workforce and the facilities that support it, and the shared data structures that keep every agency on one truth model.

Around that, deliver the access control that decides who may see and change what across a multi-agency platform, the validation that keeps the data correct under national load, the reporting stack that turns raw inputs into operational decisions, and a process that can survive the realities of public-sector rollout without breaking on change requests.

Action

Built the national learners module in Laravel and Vue 3 on a PostgreSQL schema exceeding 1,000 tables, and integrated NIRA (the National Identification and Registration Authority) national identity verification at the point of record creation so the classroom roster is anchored to a real person rather than a spreadsheet guess.

Extended the same foundation across the sector. The HR module centralised nationwide staff records and integrated national workforce management, work permit and refugee data sources. The Admissions and examinations modules tied school-level operations directly to the ministries that rely on them, while the national data-validation workflow prevented inconsistent districts from shipping bad figures upward.

Designed and documented the data model, the 1,000+ table schema, the SRS (Software Requirements Specification) and the system architecture that became the reference specification for the deployment. Every agency on the platform works from the same canonical definitions, which is how the ministry can trust what it sees.

Built the cross-agency integrations as services in Laravel, Go and Python FastAPI, connecting NIRA for identity, UNEB (the Uganda National Examinations Board) for examinations, TMIS (the Teacher Management Information System) for staffing, and multiple ministry datasets that were previously impossible to reconcile in real time.

Implemented role and permission management so ministry headquarters, district officials and individual schools each see and edit only their own scope, secured with OAuth 2.0 and token-based access patterns that are easy to audit and harder to misconfigure.

Built the DEO (District Education Officer) data validation workflow that stands between a school's submission and the national figures. A school enters its own enrolment, staffing and facility data, and the validation layer checks it before it becomes authoritative at district or national level, which keeps bad submissions from silently polluting the reports.

Delivered the interfaces ministry staff and district officers use daily: data-heavy dashboards and validated bulk-entry forms built in Vue 3 with Pinia and TanStack Query, which keep server load predictable even when the platform is under national reporting pressure.

Laid the documentation foundation the platform is still built on. Architecture decision records capture why each load-bearing choice was made and what was rejected, so a decision can be traced and changed without re-deriving the whole system from scratch.

National EMIS — Container diagram, scope: the learner and workforce platformContainer diagram of Uganda's national Education Management Information System. Schools and district education officers authenticate with OAuth 2.0 and enter learner, staffing and facility data through a Vue 3 client into the Laravel learners module. Every write passes through an ingestion gate that calls NIRA, the National Identification and Registration Authority, to verify identity before the record is accepted, so duplicates are rejected at source rather than cleaned up downstream. A District Education Officer then reviews each school submission and approves or rejects it before anything aggregates upward, with every decision written to an audit log. Accepted records land in a PostgreSQL schema exceeding 1,000 tables that also backs the HR, administrative units, infrastructure and water and sanitation modules. Long-running imports, exports and notifications run on queue-backed workers outside the request cycle. Integration services written in Laravel, Go and Python FastAPI connect NIRA, UNEB, TMIS, the Office of the Prime Minister and the Ministry of Finance behind deliberately tolerant clients that apply retries, timeouts, idempotency keys and reconciliation, so a partner outage degrades a sync rather than corrupting local data. A separate unauthenticated read-only path serves public search across more than 90,000 school records.School[Person: submits own data]District officer[Person: DEO — reviews]Citizen[Person: unauthenticated]OAuth 2.0OAuth 2.0trust boundary — authenticated, role-scoped, audit-loggedLearners module[Container: Laravel · Vue 3]national registrationlearner recordIngestion gate[Component: on-write check]rejects duplicatessubmissionDEO review[Component: approval gate]approve · query · rejectapproved onlyQueue workers[Container: imports · exports]outside request cyclePostgreSQL[1,000+ tables · 30M+ learners]write · verifiedbulk loadAudit log[who approved what, when]reading the same store: HR · Admin Units · Infrastructure · Water & Sanitationrole scopes: ministry HQ · district · school — each sees only its own dataserver-side pagination and query optimisation · SRS, ERDs and ADRs are the reference specpublic — unauthenticated, read-onlyPublic school search[Container: 90,000+ school records]search, no loginpublished fields onlyverify identityexternal agencies — behind deliberately tolerant clientsNIRAnational identityUNEBexaminationsTMISteacher managementOffice of the Prime Ministercross-agency dataMinistry of Financebudget and planningretries · timeoutsidempotency keysreconciliationintegration services: Laravel · Go · Python FastAPI — a partner outage degrades a sync, it does not corrupt local dataKEYsolid arrow = synchronous call, labelled with what crossesdashed box = trust boundary — authorisation changes herecylinder = datastorestadium = external agency, outside this platformteal fill = the ingestion gate — identity is verified before a write lands[Type: tech] = C4 element type and its technologyNIRA = National Identification and Registration AuthorityUNEB = Uganda National Examinations BoardTMIS = Teacher Management Information SystemDEO = District Education Officer

Result

30M+ learner records registered across every school level in Uganda, with duplicate registrations eliminated at source rather than cleaned up downstream. Learner, staffing, facility and district records flow into one national model instead of into a set of reconciled spreadsheets with a different truth in each office.

More than 90,000 school records are searchable by the public, and the schema and architecture documentation became the reference specification new engineers join the platform through — meaning the system is not just operational, but maintainable and understandable long after the first rollout.

  • Laravel
  • Go
  • Vue 3
  • TanStack Query
  • PostgreSQL
  • FastAPI
  • RBAC
  • Data Validation
  • Architecture Decision Records

IMPALA LITE2 — Multi-Tenant Waste-Management SaaS

Uganda, extending to Malawi

Co-lead Engineer — SMS ONE (U) Limited

Situation

Licensed private waste-collection companies ran their operations across disconnected tools — scheduling in one place, fleet somewhere else, billing and accounting in spreadsheets. No single system reflected the actual business reality, so the operational picture was both incomplete and late.

The regulatory shape made it harder. An operator works across several city authorities, each licensing and reporting on it separately, and money moves along two entirely different paths: the platform must track operational service separately from the money relationship that binds the tenant to the authority.

Task

Build a multi-tenant, multi-territory platform where each licensed operator runs its entire business — customers, scheduling, fleet, billing, payments and accounting — on its own subdomain, while the control plane handles onboarding, billing and shared infrastructure without letting the two worlds contaminate each other.

Sit that under a control plane that onboards tenants, bills them for the platform and runs the shared infrastructure, while keeping the two money relationships strictly separate so the platform can meet regulation without creating a compliance incident in the accounting layer.

Action

Built a Go modular monolith on Chi spanning 44 domain modules — operations, scheduling, fleet, containers, customers, contracts, procurement, payroll, HR, billing, payments, reconciliation and reporting — so the system could scale without the complexity tax of a distributed service mesh before it was needed.

Isolated tenants with schema-per-tenant PostgreSQL backed by row-level security as a second line of defence, so a query bug cannot leak across tenants even if it escapes the schema boundary.

Implemented a native double-entry general ledger rather than bolting reporting onto invoice tables. It is multi-currency, tax-aware and billing-source-agnostic, so revenue recognised from a contract, a levy or a platform fee is consistent, explainable and auditable.

Deployed on Cloud Run in africa-south1 for on-continent data residency, and drew the module boundaries so Payments, Telematics and the shared premise registry can be carved out into independent services when the traffic and compliance envelope demand it.

IMPALA LITE2 — Container diagram, scope: the multi-tenant SaaS platformContainer diagram of IMPALA LITE2, a multi-tenant waste-management SaaS serving Uganda and Malawi. Vue 3 web and Flutter field clients authenticate through Keycloak using OpenID Connect, with one Keycloak organisation per tenant. Requests reach a Go modular monolith built on the Chi router, deployed to Cloud Run in the africa-south1 region for on-continent data residency. The monolith spans 44 domain modules covering operations, scheduling, fleet, containers, customers, contracts, procurement, payroll, HR, billing, payments, reconciliation and accounting, exposing 471 routes behind a generated OpenAPI 3.1 contract. Tenant isolation is defence in depth: each tenant has its own PostgreSQL schema, with row-level security as a second line of defence so a query bug cannot leak across tenants even if it escapes the schema boundary. Cross-module communication uses a transactional outbox that commits in the same transaction as the data producing it, then publishes onto Google Cloud Pub/Sub, so a crash between write and publish cannot desynchronise the system. A native double-entry general ledger, multi-currency and tax-aware, receives postings from every billing source, and per-country fiscalisation plugs into it: EFRIS for Uganda and the Malawi Revenue Authority for Malawi, alongside Mobile Money settlement with pricing denominated first in Ugandan Shillings. Payments, Telematics and a shared resolve-only premise registry are drawn with carve-out seams, since the module boundaries were set so they can become independent services later without a rewrite.Operator web[Container: Vue 3 · Pinia]Field app[Container: Flutter]Keycloak[Container: OIDC]one org per tenantHTTPS · JSONHTTPS · JSONverifies tokenCloud Run · africa-south1 — on-continent data residencyGo modular monolith[Container: Go · Chi]44 domain modules · 471 routesOpenAPI 3.1 contract[Component: generated from code]documented by constructionoperations · scheduling · fleet · containers · customers · contractsprocurement · payroll · HR · billing · payments · reconciliation · accountingtenant-scoped queryisolation — defence in depthSchema-per-tenant PostgreSQL[one schema per tenant]Outbox[same transaction]commit or neitherwritten with the data+ row-level security — the second lineif a query escapes the schemapublish · at-least-oncePub/Sub[Container: Google Cloud Pub/Sub]postingsmoney — one set of books for every billing sourceDouble-entry ledger[Component: multi-currency · tax-aware]billing-source-agnosticSettlement[Component: Mobile Money]priced first in UGXsettlesfiscalise each invoiceexternal — per-country tax authoritiesEFRISUganda Revenue AuthorityMRAMalawi Revenue Authoritycarve-out seams — extractable later without a rewritePayments[Component: module boundary drawn]Telematics[Component: module boundary drawn]Premise registry[Component: shared · resolve-only]a customer movesbetween operatorswithout addresshistory being lostKEYsolid arrow = synchronous call, labelled with intent and protocoldashed arrow = asynchronous publishdashed box = deployment, isolation or extraction boundarycylinder / barred box = datastore / ordered, replayable logstadium = external system outside this platformOIDC · EFRIS = OpenID Connect · Electronic Fiscal Receipting and Invoicing SolutionMRA · UGX = Malawi Revenue Authority · Ugandan Shilling

Result

A tenant runs its entire operation on its own subdomain, with the ledger balanced and the fiscalisation correct for its country, while the control plane onboards and bills operators independently without mixing operational data with financial control data.

Built as a modular monolith with service boundaries already drawn, so the platform can be decomposed under load rather than rewritten.

  • Go
  • Chi
  • PostgreSQL
  • Keycloak
  • Pub/Sub
  • Vue 3
  • Flutter
  • GCP

Bulk SMS Platform — Parliament of Uganda

Parliament of Uganda

Software Engineer — SMS ONE (U) Limited

Situation

Parliament of Uganda needed to reach very large contact bases reliably, on a platform where a failed dispatch is visible institutionally rather than quietly retried. Government communication has no tolerance for silently dropped messages.

The volume breaks the naive approach twice over. Per-message synchronous delivery collapses long before the contact base is exhausted, and a bulk upload of tens of millions of rows cannot be processed inside a request without the upload itself timing out.

Task

Build ingestion capable of absorbing tens of millions of contacts and delivery capable of dispatching against them without degrading the rest of the platform, under strict government security standards.

Make the pipeline observable enough to prove it is healthy — not merely to debug it after a failure, but to see saturation coming while there is still time to act.

Action

Built structured bulk ingestion for large contact uploads and a contact-management module that segments recipients by administrative unit, so a message can target a district or constituency precisely rather than being broadcast at everyone. Ingestion validates and stages rows outside the request cycle, so an upload of tens of millions of contacts never blocks the user who started it.

Moved delivery onto Kafka, decoupling ingestion from dispatch. Producers write regardless of how fast consumers drain, so a slow gateway or a burst of traffic degrades throughput instead of losing messages — the failure mode becomes a growing backlog, which is visible and recoverable, rather than silent message loss, which is neither.

Implemented and supported the delivery path itself through Kannel and SMPP (Short Message Peer-to-Peer) gateways, diagnosing delivery failures, throughput bottlenecks and binding faults down to the gateway session level, and sustaining real-time delivery under the security standards government messaging is held to.

Instrumented the pipeline with Prometheus metrics and built Grafana dashboards over ingestion rate, consumer lag and delivery outcomes. Consumer lag is the signal that matters: it rises before delivery starts failing, so the dashboards show a problem forming rather than reporting one that has already happened.

Bulk SMS Platform — Container diagram, scope: ingestion to handsetContainer diagram of the Parliament of Uganda bulk messaging platform. A staff user uploads a large contact file, which the ingestion service validates and stages outside the request cycle, so an upload of tens of millions of rows never blocks the person who started it. Contacts are held in PostgreSQL, more than 5 million of them, and a contact-management module segments recipients by administrative unit so a message can target a district or constituency precisely rather than broadcasting to everyone. Campaign dispatch is decoupled from ingestion by a Kafka log: producers write regardless of how fast consumers drain, so a slow gateway or a burst of traffic degrades throughput into a visible, recoverable backlog rather than losing messages silently. Consumer workers read from the log and deliver through Kannel and SMPP gateways, maintaining bind sessions to the operator. The pipeline is instrumented with Prometheus and visualised in Grafana over ingestion rate, consumer lag and delivery outcomes; consumer lag is the signal that matters because it rises before delivery begins to fail, showing a problem forming rather than reporting one that has already happened. The architecture is designed for 100,000 or more concurrent users.Parliament staff[Person: uploads campaigns]bulk contact fileingestion — validated and staged outside the request cycleIngestion[Container: validate · stage]never blocks the uploaderSegmentation[Component: admin unit]district · constituencysegmentsPostgreSQL[5M+ contacts]staged rowstarget a district or constituencyprecisely, rather than broadcastingat everyonean upload of tens of millions of rows never blocks the user who started itenqueue campaignKafka[partitioned, replayable log]producers write regardless of drain ratefailure mode by design:a slow gateway grows avisible, recoverable backloginstead of losing messagessilently, which is neitherconsume · at-least-oncedispatch — delivery path to the handsetWorkers[Container: Go consumers]scale with lagSMPP bindKannel · SMPP gatewayoperator bind sessiondiagnosed down to gateway session level: bind faults · throughput · delivery receiptsscrapedobservability — see the problem forming, not afterPrometheus[Container: metrics]scrapes the pipelineGrafana[Container: dashboards]ingest · lag · deliveryqueriesconsumer lag is theleading indicatorit rises before deliverystarts failingdesigned for100,000+concurrent usersKEYsolid arrow = synchronous calldashed arrow = asynchronous consumebarred box = ordered, replayable logcylinder = datastorestadium = external operator gatewaySMPP = Short Message Peer-to-Peer protocol

Result

5M+ contacts ingested, on an architecture designed for 100k concurrent users, with delivery decoupled from ingestion so neither can take the other down.

The pipeline is observable end to end, so saturation surfaces as a rising lag graph rather than as undelivered messages discovered after the fact.

  • Laravel
  • Go
  • Kafka
  • SMPP
  • Kannel
  • PostgreSQL
  • Prometheus
  • Grafana

These platforms are proprietary and closed-source, so the code is described rather than linked.

More projects on GitHub

Testimonials

Job took ownership of our database infrastructure and kept it running without interruption. He had a habit of tracing problems to the actual cause instead of patching symptoms, and recurring issues simply stopped coming back. He also led our engineering team through a major delivery on time and on budget.
Rwakasisi Edwin— LinkedIn profileSenior Software Engineer · GT Bank
Job built core parts of the national EMIS platform, including the integrations with NIRA and UNEB that validate records at the point of entry. He is careful with data at a scale where mistakes are expensive, and he documents his work so the rest of the team can rely on it.
Clinton Nkesiga— LinkedIn profileSenior Software Engineer · SMS ONE (U) Limited
Job was among the strongest engineers I taught. He went well past the syllabus into architecture and system design, and he was already building complete production systems before he graduated.
Taqee Ahmed— LinkedIn profileSenior Software Engineer · Sai Pali Institute of Technology & Science
Job designs for the system he will have in two years, not the one in front of him. He drew clean service boundaries across our platform work and kept the contracts between modules explicit, so the parts that later needed to scale independently could be pulled out without a rewrite.
Alfred Oked— LinkedIn profileTech Lead · SMS ONE (U) Limited

Technical Stack

Core
GoJavaTypeScriptJavaScriptPHP
Backend Engineering
LaravelGoJava / Spring BootNestJSDjangoREST API DesignMicroservicesAsynchronous Queues
Frontend Architecture
Vue.jsReactReact NativeFlutterNext.jsTypeScriptJavaScript (ES6+)Tailwind CSS
Data & Infrastructure
PostgreSQLMySQLMongoDBRedisKafkaRabbitMQDockerKubernetesTerraformAWSGCPPrometheusGrafanaElastic StackKeycloakCI/CDLinux

About

I am a software engineer based in Kampala, Uganda. I build systems where being wrong is expensive — national platforms for public institutions, and enterprise products for private companies. The public work has meant identity verification, education data and examinations. The commercial work has meant multi-tenant SaaS that runs a company end to end: operations, fleet, procurement, payroll, billing and a double-entry ledger that has to balance in every currency and satisfy each country's tax authority.

My day-to-day is built around Go, Java, TypeScript, JavaScript, PHP, Laravel, NestJS, Spring Boot, Django, Vue.js, React, React Native, Flutter, Next.js, and Tailwind CSS, with a strong focus on backend engineering, REST API design, microservices, asynchronous queues, and frontend architecture. I care most about the parts that are hard to retrofit — schema design that survives a decade of policy change, tenant isolation you can prove rather than hope for, validation enforced at ingestion rather than patched downstream, queue architecture that degrades gracefully instead of collapsing under load, and platform choices that keep a system dependable under real production pressure.

I work equally well embedded in a distributed remote team and on the ground with an enterprise client. I also build and maintain open-source tooling, and I write regularly about engineering practice and technology trends for the DEV Community and for TechTalk, my LinkedIn newsletter.

Hiring for a global team?

I work effectively with distributed engineering teams, bringing strong communication, reliability, and a structured approach that makes collaboration seamless across time zones.

[See how I work remotely]

Diploma in Software Engineering —Sai Pali Institute of Technology & Science, Entebbe, Uganda.

Bachelor's of Science in Computer Science (In Pursuit) —University of the People (USA).

Contact

Let's discuss what you're building.

I'm open to software engineering opportunities, technical collaborations, and challenging engineering projects involving scalable applications, distributed systems, backend architecture, and modern web technologies.

For professional opportunities, project collaborations, or technical discussions, reach me directly by email, WhatsApp, phone, through LinkedIn, or .

Available for full-time remote roles, contract engagements and enterprise consultation. (How I work remotely)