← All work

— Case Study 04

J.E.R.R.Y. Support Assistant

Contributor work on a team Python/Qt project.

Role
Contributor
Year
2026
Team
4-person team
Status
shipped
Stack
PythonPySide6reportlabopenpyxl

The brief

J.E.R.R.Y. is an IT troubleshooting and support assistant — a desktop application built in Python with PySide6, the official Qt binding for Python. It runs as a native app on Windows, Linux, and macOS, designed to help support staff (and the people they're helping) work through common technical issues without bouncing between browser tabs and knowledge bases.

I joined as one of four developers on a class team. The architecture, the product direction, and the day-to-day project lead were not mine. Teammates: Aaron Hirtle, Morgan Perry, Joseph Wilson, and me. The repo is private, so I can't link to the code — but two of the issues I owned are worth talking about because they cover very different technical territory.

What I shipped

Two PRs landed under my name and they're the right ones to focus on.

Issue #11 — UI overhaul. A full visual pass on the application, replacing the original ad-hoc styling with a unified design token system implemented in styles.py. Tailwind's slate/blue palette as the source of truth, with a single module that every widget pulls from.

Issue #4 — PDF and Excel ticket export. Users needed to be able to dump a ticket — or a batch of tickets — as either a formatted PDF (for printing or emailing) or a tabular Excel file (for reporting or analysis). I built both export paths on top of reportlab (PDF) and openpyxl (Excel), sharing a common data shape so the same ticket structure flows into either output without duplicating logic.

Bringing web tokens to a Qt app

The original UI had been built ad-hoc — colors and spacings declared inline wherever they were needed, no shared vocabulary across views. For a support tool that has to feel consistent and professional across multiple screens, that's a structural problem long before it's an aesthetic one.

PySide6 has its own stylesheet language — Qt Style Sheets, or QSS — which is CSS-shaped but not CSS. You can write QSS directly, but you can also build it programmatically from Python values, which is what makes design tokens possible. The pattern looks roughly like this:

# styles.py
PRIMARY   = "#3b82f6"   # Tailwind blue-500
SLATE_BG  = "#0f172a"   # slate-900
SLATE_FG  = "#e2e8f0"   # slate-200
RADIUS    = "6px"
PAD_Y     = "8px"
PAD_X     = "16px"

BUTTON = f"""
QPushButton {{
    background-color: {PRIMARY};
    color: white;
    border-radius: {RADIUS};
    padding: {PAD_Y} {PAD_X};
}}
"""

Every widget that needs a color, radius, or spacing pulls from the same source. Slate/blue as the palette wasn't arbitrary: it reads as "professional IT tool" without being boring, it's well-tested for accessible contrast, and most of the team already knew Tailwind class names — so when someone wanted "a slate-700 background", the mental model transferred straight from web work into the Qt codebase.

The bigger point here is that design-system thinking — tokens, named palettes, reusable component styles — isn't just for the web. Desktop GUI frameworks rarely use that vocabulary, but the concepts work and they make the codebase much easier for the rest of the team to extend without me babysitting every new screen.

The export pipeline (and why it was hard to test)

Programmatic document generation is one of those areas that looks simple until you actually try to write reliable tests for it. reportlab builds PDFs as a sequence of flowables — paragraphs, tables, page breaks — and lets the library handle layout and pagination. openpyxl builds workbooks cell-by-cell, with most of the interesting work in formatting (header rows bold, timestamps actually typed as dates rather than strings, number columns aligning correctly). Both are pure-Python and both produce real, inspectable artifacts.

The hard part was testing them inside a Qt application. PySide6's whole interaction model is signals and slots — events flowing through a QApplication event loop — and that event loop doesn't run happily on headless CI runners. The first version of the export feature, with logic and Qt integration tangled together, was nearly impossible to test in CI.

What got it there in the end was decoupling: the actual export logic became pure functions on plain ticket data, and the Qt layer became a thin shell that calls those functions in response to a button click. The export functions are testable in isolation; the Qt wiring is small enough to verify by hand. That separation came out of a few rounds of PM code review, and it's the right shape in hindsight even though I didn't see it that way at first.

What it taught me

Design systems aren't just for the web. Tokens, palettes, named spacings, reusable component styles — every UI benefits from the same vocabulary. The fact that desktop frameworks rarely talk about "design tokens" is a vocabulary gap, not a capability gap.

Code review is a feature, not a tax. The PM on this project ran a strict review process — multiple rounds, real change requests, no rubber-stamping. Annoying in the moment and absolutely the right move. My export pipeline is testable today specifically because someone with more experience pushed back on the first version. The lesson generalizes: the version of the code that ships after three rounds of review is almost always better than the version that would have shipped after one.

Be honest about scope. I was a contributor on a team project. The PRs that shipped under my name are mine to talk about; the rest of the system isn't. Being clear about that — in conversations, on a portfolio, in a job interview — is just good practice. Inflating contribution is the kind of thing that catches up with you in technical interviews.