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. Next, I want to join a team of experienced developers and learn from their reviews of my code. 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. I designed and built its reporting platform, which syncs data from nine vendor systems into one database and turns that data into a monthly report for each of the company's roughly 40 clients.
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
- Reports generate roughly twice as fast as on the legacy system, and 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, which is why working with experienced developers is a large part of what I want next.
Service desk administration
Alongside the engine, 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.
-
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 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, a syllabus 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
- +20%
- I raised the average percentage for IT by over 20%.
-
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, so 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 engine. I worked out how each of them should work 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. Loading 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 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, and a stalled page is retried 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, and clears a keyless table's date range before reloading it. 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 that 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. When I measured the warehouse's history, 588,000 stored versions held only 50,000 distinct states, so a new version is kept 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
Adding a new kind of data from one vendor takes three things: a query file, a mapping from the query's columns to a Java record, and the table that the rows land in. 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.
- 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 hold 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 engine draws each chart as SVG from a client's data, with no chart library: eight chart types, from bar and line charts to gauges, each written in Java. Drawing them in the engine 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 the engine 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 engine is a set of libraries that its apps call, 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 its sources in the order their data depends on each other, and runs independent sources at the same time on virtual threads. The library modules seal 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. 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 engine 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 its data does not reach. Its 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
I write command-line tools in Rust for jobs that are specific to the engine's codebase and slow or error-prone by hand. One builds and seeds its test database from the schema, and another updates the version tags in its Javadoc.
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
-
Slicket
A ticketing system in Rust, designed around an entity component system, with every AI contribution in its own commit.
Read more
Slicket is a ticketing system I am building in Rust. I administer a well-known service desk system at work and find it complicated to use. Slicket is my attempt at one that feels better. Its backend is designed around an entity component system (ECS), a design where each thing, such as a ticket, is a bare ID with its data stored in separate components. Systems then run the logic over every ID that has the components they need. It is at an early stage of design: the entity and component types are built, and the storage and systems come next.
It also shows how I work with AI. On its
devbranch, my work and the AI's go in separate commits, and each commit's author records whose work it contains. Anyone can see exactly where I used AI.
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 project 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).