Java · SQL Server · Backend and data
Kheagen Haskins
Backend and data developer working in Java and SQL Server. I am the only developer at a managed IT services provider, where I designed and built the platform that syncs data from nine vendor systems and produces every client's monthly report. I came to software through teaching Java.
- South Africa
- slinky.haskins@gmail.com
- GitHub
- References and full contact details are in my private CV, available on request.
Experience
-
Managed IT services provider, Johannesburg and Durban
I am the company's only developer. The company has roughly 40 clients. I designed and built its reporting platform, which syncs data from nine vendor systems into one database. The platform then turns that data into a monthly report for each client.
Responsibilities
-
Software engineer
I write all of the code, from the SQL that builds the database to the web server that runs the reports.
- SQL schema
- SQL queries
- Java data access
- PDF reports
- Web server
-
Database administrator
I created the SQL Server database, change it as the platform grows, and keep it healthy.
-
Tester
I write and run every test, and I configured the CI/CD pipeline that runs them.
-
DevOps
I built the pipeline that tests, builds and deploys the platform to production. A runbook I wrote covers recovery by hand.
Achievements
- Report speed
- 2×
-
SpeedData per report
- Legacy system
- My platform
- The platform generates reports roughly twice as fast as the legacy system did, and they include almost twice as much data.
- Deployment
- Automated
- Every deploy runs the tests and the build on GitHub first. On the server, it waits for any running sync to finish, then checks the new version's health. A deploy that fails any step rolls itself back, and GitHub emails me the failure.
How I review my code
As the company's sole developer, I review all of my own code. My experience of peer review comes from teaching, where senior teachers moderated my exam papers and I moderated theirs. At work, I run layered AI audits, each covering one concern: correctness, conventions, documentation or test coverage. I repeat them until the findings turn trivial. They catch real mistakes, but a senior engineer sees what they miss.
Service desk administration
Alongside the platform, I am the system administrator for the company's service desk system. I configure its ticket types and workflows, and take on the occasional ticket that needs a developer.
Programs for client machines
I wrote two programs in Rust that the company installs on its clients' machines through its remote management tool.
The first asks Windows users to restart once an update is waiting, in place of the generic Windows prompt. The management tool starts it under the SYSTEM account, in a background session that the user cannot see. The program therefore finds the user at the physical screen, and starts a second copy of itself on that user's desktop through the Win32 API. The prompt waits while the user is presenting, and lets them postpone the restart. Each outcome goes into the registry and the exit code, where the management tool reads it. A release build is one
.exethat runs on Windows 10 and 11 with nothing else installed.The second is a macOS menu bar icon that opens the company's self-service portal. GitHub Actions builds its installer on a Mac runner, for both Apple Silicon and Intel. The installer sets the icon to start at every login. An uninstall script also reports when the management tool has put the icon back. The program's repository is public, and my private CV links to it.
-
Software engineer
Other roles
-
Tutoring company
Java, SQL and Python tutoring for high school and university students.Alongside my development work I tutor students from Grade 10 on the Independent Examinations Board (IEB) syllabus up to their first years at university. Most of the work is diagnosis. I find the idea a student has actually misunderstood, then explain it in terms they already understand.
Responsibilities
-
Tutor
I explain and teach Java, SQL and Python to students at every level.
-
Tutor
-
Independent school, Johannesburg
Taught IEB Information Technology in Java, and wrote the textbook for the course.I joined in 2020 to teach coding and robotics to primary school children, using LEGO Mindstorms EV3 robots and Swift Playgrounds. From 2021 I also taught IEB Information Technology to Grades 10 to 12. That syllabus is taught in Java. I wrote a textbook for the course and coached matric students through their final-year projects.
Responsibilities
-
Curriculum designer
When Covid moved lessons online, I wrote the school's coding and robotics syllabus from scratch, and the school kept it after Covid ended. By Grade 9, students had published their own CV website.
- Grade 3: binary
- Grade 4: HTML
- Grade 9: a live CV in HTML, CSS and JavaScript
-
Examiner
I set every test and exam I gave. Senior IT teachers moderated each paper, round after round, until they found nothing left to fix.
-
Moderator
I moderated many other IT teachers' exams, as well as the school's own Computer Applications Technology (CAT) exams.
-
Remedial educator
I taught coding successfully to students with a range of disabilities, including cerebral palsy and autism.
Achievements
- IT average
- 40% to 60%
- I raised the class average for IT from 40% to 60%.
-
Curriculum designer
-
Royal Sitting Services
Co-founded a pet-sitting business that grew past what two people could run.My partner and I started a pet-sitting business while I was at college. Bookings filled up faster than the two of us could take them. As a result, we interviewed and hired more sitters to keep up. Covid ended the business in 2020, just as I began teaching.
Hard problems
These are the hardest problems I have solved in the reporting platform. I worked out the solution to each one myself. My starting point was the old reports, and sometimes the API behind their data.
-
Syncing from a data warehouse billed per query
The hardest source to sync was a data warehouse billed per query. The code that loads its tables into an empty database was quick to build. Keeping them up to date afterwards was the real work, and each fix exposed the next problem.
-
Schema changes
The vendor changed it without notice
The vendor changed its schema without notice. Before the sync fetches anything, it now checks that it can map every column that it will write, and it reports a mismatch as a schema failure.
-
Slow or hung
A slow response looked like a dead one
Syncs began to hang, and nothing could tell a slow response from a dead one. I moved to reading 50,000 rows a page, with a time limit on each page. The sync retries a stalled page on a fresh connection. Paging then raised the questions every sync has to answer, such as what to roll back when the sixth page fails, and what to do with half a page. Each page commits on its own, and a half-read page is rolled back. The sync inserts or updates each row by its key. For a table without a key, it deletes the rows in the date range, then reloads them. As a result, the next run can re-read from the last successful point without duplicating anything. The sync moves that point forward only after the whole run has succeeded.
-
Measuring
A few tables took far longer
I built diagnostic tables into the database. They record each sync's errors and the time taken for every source and table. A day or two of production data showed a few tables taking far longer than the rest. I therefore moved those tables onto a schedule of their own, reading them four times a month to save warehouse credit.
-
Finding changed rows
Reading whole tables cost too much
Reading every table whole on every run cost too much on the largest ones. Where a table has change-tracking columns, the sync now pulls only the rows that changed since its last run, in pages sorted on the table's key. A table without them is read whole, and the largest of those are read at most once a day.
-
History
A late request for every record's past
To meet a late request for the history of every record, I added slowly changing dimension (SCD) tracking to the warehouse's three main tables and to seven tables from a second source. It keeps each past version of a record alongside the current one. I measured the warehouse's history and found 588,000 stored versions that contained only 50,000 distinct states. As a result, the tracking keeps a new version only when a record actually changes. Each table has its own tracking policy for now, and I am working towards a general set of rules that new tables can follow.
-
Schema changes
The vendor changed it without notice
-
Adding vendor data without new API code
The vendor's REST API returned records one client and one page at a time, meaning collecting every ticket took hundreds of requests in a row. I found that the vendor also offers a reporting endpoint, which runs a SQL query and returns every matching row at once. The sync now collects its data through that endpoint, with one query for each kind of data. Adding a new kind of data from that vendor takes three things: a query file, a mapping from the query's columns to a Java record, and the table that stores the rows.
- Ticket collection
- 2.8 s
- Collecting about 840 tickets took 8.8 minutes through the paged REST calls, and takes 2.8 seconds through one query.
-
Separating the reports from the schema
My first classes copied their tables. As a result, a changed table meant a redesigned class. Every piece of code that used the class then changed with it. I split the data layer into thirteen areas. Eleven match a part of the report, such as tickets, hardware, backups, patches, email security and security training. The other two store the client list and each report's saved settings. Each area is an interface that returns typed records, and its SQL and implementation stay hidden behind it. A change to the database is now made behind those interfaces, where the reports never see it.
-
Branded charts in PDF reports
The reports needed charts in the company's branding. The platform draws each chart as SVG from a client's data, with no chart library. It has eight chart types, from bar and line charts to gauges, each written in Java. Drawing them in the platform gives it control over every colour, font and label in a chart. The cover page, stat cards and score boxes are SVG files with slots that the platform fills, so marketing can change their layout by editing a file. A report that still contains an unfilled slot fails rather than reaching a client.
Skills
-
Java
The platform is a set of libraries that its apps call. It is built on the standard Java APIs and a few small, focused libraries. I wire it together by hand with plain constructor calls. Spring suits many projects. This one needed the freedom to change shape early, and code that joins two libraries is easy to replace.
The sync's classes receive their dependencies through their constructors, meaning tests can pass in their own doubles. The sync runs each source after the sources whose data it depends on, and runs independent sources at the same time on virtual threads. The library modules hide their internals under JPMS, the Java module system. As a result, one module reaches another only through what that module exports.
-
Data
Hover to read how I use itTap to read how I use it
Data
I wrote the code that generates the sync's SQL from the schema and protects it from SQL injection, because jOOQ, the best-known library for this, needs a paid licence for SQL Server. Where a source reports what changed, the sync loads only new and changed rows. A stalled query is retried on a fresh connection, and a table that fails catches up on the next run.
-
APIs
Hover to read how I use itTap to read how I use it
APIs
Most vendor connections are rate-limited and retry failed calls with growing delays, waiting without holding a thread. Login tokens refresh safely while many calls run at once. Guards on the connections reject any call that could change data in a vendor's system, keeping every sync read-only.
-
Reports and apps
Hover to read how I use itTap to read how I use it
Reports and apps
The platform produces Excel workbooks and PDF reports with charts. Every report keeps the same two rules: no section is ever left out, and no figure is shown for a period that its data does not reach. The platform's apps ship as standalone programs that bundle their own Java runtime.
-
Tooling
Hover to read how I use itTap to read how I use it
Tooling
Every Maven module takes its dependency versions from one shared bill of materials (BOM). Eight modules fail the build when their test coverage drops below a set minimum, and Checkstyle, PMD and Spotless check the code's style. Tests also enforce the architecture: SQL has to live in its own files, and the library modules cannot pull in a logging backend. SQL Server runs in Docker for development and testing, and GitHub Actions builds and deploys the apps.
Projects
-
Sarx
A god game in Rust, with the renderer and entity component system written by hand to learn how each one works.
Read more
Sarx is a god game that I am building in Rust. The player guides a civilisation, trains a creature and competes with rival factions for the land. Many older god and strategy games inspired it.
The project has two goals of equal weight: the game itself, and understanding the graphics pipeline and the architecture of a game engine. For that reason, I do not use a game engine, and I write the layers below the gameplay myself. wgpu, a Rust graphics library, handles the differences between graphics APIs, and the pipeline, shaders and draw calls are my own code.
The simulation will run on an entity component system (ECS), a design where each thing, such as a villager, is a bare ID with its data stored in separate components. My ECS stores each entity in a table for its exact set of components. The project is at an early stage: the game opens a window and draws a triangle, and the ECS has its entity type, component storage and tables. Queries and the fixed-step simulation come next.
I write nearly all of the code myself, because I want to understand every line. AI explains concepts, reviews my work and writes much of the documentation. I record each library and design decision in a short decision record, stating what I decided and why.
Education
-
2020 to present
Self-taught programming
I learnt to program through my teaching, my development work and my own projects. I also relearnt maths on Brilliant, to use it as a tool. Each step led into the next.
- Noise generator Draws natural-looking textures and terrain from maths
- Maths The maths behind the noise
- ECS architecture The way data is laid out drives the design
-
to
Professional Java
After leaving teaching, I spent most of a year learning Java as industry uses it rather than as a school syllabus teaches it. I learnt Git, and worked through Codecademy's courses on Java and on the Spring framework.
-
to
Bachelor of Arts
I completed a three-year BA at Varsity College Sandton (now Emeris), majoring in Psychology and Communication Science.
-
2014
Matric
I earned my National Senior Certificate at Maragon Private Schools (now Crawford Ruimsig).