Java · SQL · Full-stack development
Kheagen Haskins
Full-stack developer working in Java and SQL, with most of my work on the backend. I am the only developer at a managed IT services provider, where I designed and built the reporting engine that produces every client's monthly report. Teaching is how I got here. A psychology degree led me into it, and teaching Java led me into building software full time.
- 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
I designed and built the company's reporting engine. It runs in production and covers every device the company manages, from laptops and servers to printers and switches. I also run it. Scheduled tasks sync every source every four hours and restart the report server if it stops, and a runbook I wrote covers each deployment.
- Design decisions
- 24
- Each one is recorded in an architecture decision record, and each one is mine. Three of them are still proposals.
- Lines of code
- 84,000
-
- Java 73,000
- SQL 5,000
- Other 6,000
- The count leaves out blank lines and comments. Half of the Java is tests.
- Time for 100 reports
- 20 min
-
- Engine 20 min
- Old tool over 1 h
- Measured before any work on the engine's speed.
One half of the engine syncs data from nine vendor systems into one database. The other half reads that data back out and turns it into configurable reports.
-
9 data sources
Ticketing, monitoring, security, backups, training
I built the API layers that read the company's ticketing, monitoring, security, backup and training systems over REST, GraphQL and JSON-RPC, and its data warehouse over JDBC.
-
1 database
SQL Server, synced on a schedule
I designed the schema and administer the SQL Server database.
-
Domain model
Tickets, hardware, backups, patches
I designed the layer the reports read through. Each area of the business has its own interface, which returns typed records and keeps its SQL out of sight.
-
Client reports
PDF and Excel, one per client, monthly
I design the PDF and Excel reports, laying the PDFs out in HTML and CSS. I also built the web page that configures each report.
Why the company built its own engine
The company used to build its reports with a vendor's reporting tool. The vendor then released a new analytics product, which offered new data but very little of what the old reports used. The old tool stayed the only way to reach the data the old reports used, even though the vendor had stopped maintaining it. It was full of bugs, and it depended on an API the vendor had deprecated. Our own engine copies that data into a database the company controls, so it can shape its reports however it wants.
How I review my code
As the company's only developer, I have always been the one Java engineer on the job. In place of peer review, 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. One was a one-off request to migrate a client's WordPress site. While upgrading it to a current version of PHP, I found unfamiliar files and altered settings across the site. It had gone unpatched for a long time and had been compromised, so I cleaned it out before the migration went ahead.
Other roles
-
TurtleJar
Java and Python tutoring for high school and first-year university students.Alongside my development work I tutor Java, both to Grade 10 to 12 students on the IEB syllabus and to university students taking it in their first years. I also teach Python to first years. Most of the work is diagnosis. I find the idea a student has actually misunderstood, then explain it in terms they already understand.
-
Independent school, Johannesburg
Taught IEB Information Technology in Java, and wrote the textbook for the course.From 2021 I 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, which meant explaining design, debugging and program structure to people meeting them for the first time.
I joined as a coding and robotics teacher for primary school children, using LEGO Mindstorms EV3 robots and Swift Playgrounds. When Covid moved lessons online, I taught myself web development and taught HTML and CSS to Grade 4s.
-
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.
-
Retail promotions agency
Sold GPS devices in Makro, approaching every shopper cold.I sold TomTom GPS devices to shoppers in Makro, a large retail chain, approaching each person cold. The job built my confidence in talking to strangers and persuading them.
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 that charges per query
The hardest source to sync was a Snowflake data warehouse, reached through a JDBC driver that sends each query over HTTPS. 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.
- Average update sync
- 180 s
- Across all nine sources. A routine update moves less data than a first load, but runs far more logic.
-
Testing without live calls
Every query spent paid credits
Every query spent paid credits, so I could not test against the real source. I cached one real pull as test data, then built a fake server that serves it and misbehaves on demand, by going silent or changing its schema.
-
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 paging, and found the page size the source could serve reliably. 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.
-
Measuring
Some tables took ten times 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 some tables taking ten times longer than others, so those tables now sync on their own schedule.
-
Finding changed rows
Whole-table compares failed at 4 million rows
Comparing whole tables fell over at four million rows, where one table took an hour to sync. A MERGE statement was faster but still slow. 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
A late request asked for the history of every record, so I added slowly changing dimension (SCD) tracking to the warehouse's customer, site and device tables, and to seven tables from a second source. It keeps each past version of a record alongside the current one. Each table has its own tracking policy for now, and I am working towards a general set of rules that new tables can follow.
-
Collecting tickets from the service desk
The service desk's REST API returns tickets one client and one page at a time, so collecting every ticket took hundreds of requests in a row. Its Report API runs a SQL query against the service desk's own database and returns the result as JSON. I moved ticket collection onto a single query through it. Each new kind of data now needs only a SQL file and a mapping from its columns to a Java record.
- Collecting 840 tickets
- 2.8 s
-
- SQL 2.8 s
- REST 8.8 min
- 186 times faster, as measured and recorded in the decision record that made the switch.
-
Separating the reports from the schema
My first classes copied their tables, so 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 that match what the reports cover, such as tickets, hardware, backups and patches. 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. The cover page, stat cards and score boxes are designed SVG files with slots the engine fills, so marketing can redesign them without touching the code.
Skills
-
Java
Hover to read how I use itTap to read how I use it
Java
The engine is a set of libraries that its apps call, rather than a framework that calls them. It is built on the standard Java APIs and a few small, focused libraries, and I wire it together by hand with plain constructor calls. A stack trace therefore follows my own code from the call to the failure.
The sync's classes receive their dependencies through their constructors, typed as interfaces, so tests pass in their own doubles. The modules seal their internals under JPMS, the Java module system, so 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. 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
Each vendor connection keeps to that vendor's rate limits and retries failed calls with growing delays. Login tokens refresh safely while many calls run at once.
-
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. Its apps run on Windows 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 the jobs around a build that are slow by hand or easy to get wrong. The reporting engine uses five of them. They build and seed its test database, update the version tags in its Javadoc, turn its SVG icons into Windows icons, and restart Docker when Windows sleep breaks it. The fifth counts lines of code, leaving out comments and blank lines, and it produced the line count above.
Every Maven module takes its dependency versions from one shared bill of materials (BOM). A module's build fails when its test coverage drops below a set minimum, and Checkstyle, PMD and Spotless check the code's style. SQL Server runs in Docker for development and testing.
Projects
Recent
These show how I build software now, with development experience behind me.
-
GUI Exams
Java mock exams for IEB Information Technology, where the learner's printed answers drive an animation.
Read more
GUI Exams are Java mock exams for the IEB Information Technology syllabus. The first two papers are for Grade 10 and Grade 12. In a standard paper, the learner's program prints its answers as text. Here a window reads that text line by line and animates it. In the Farm Drones paper, each line the program prints sends a drone across a farm to water a field, and a wrong battery calculation shows up as a drone falling out of the sky.
The window marks each part green, amber or red, and explains anything that is wrong.
-
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, so 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.
Earlier
I built these before I had any development experience, while I was teaching and then studying Java full time.
-
WordCheat
Lists every playable Words With Friends move, highest score first.
Read more
WordCheat is a desktop helper for the Words With Friends word game. The player drags the board and their letters into it, and it lists every move they can play, highest score first. It checks each placement against a list of about 168,000 English words. The check covers every crossing word the move forms as well, and each valid move is scored with the game's letter values and bonus squares.
I built it because my mother always beats me at the game.
-
HackMaster
The terminal hacking puzzle from Fallout 3 and 4, rebuilt as a desktop game.
Read more
HackMaster recreates the terminal hacking puzzle from the Fallout 3 and 4 video games. The player picks a password from a screen of words, and each wrong guess reports how many letters are in the right place.
Its screens update themselves whenever the game state changes, and it comes with a Windows installer.
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 so that I could 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).