Refactoring pay-calc-plus
Oct 09, 2025
Applying best practices, and gauging growth with a dirty, dusty old project.
Overview
A few years ago I created a desktop GUI to automate weekly payroll calculations. The code was awful, and I did some rather silly things. Things like using global variables, and writing functions with side effects. However, THE CODE WORKED, and I was over the moon with the fact that I had used python to automate a problem of my own.
After going to a meetup on best practices I felt inspired to refactor my project, and apply what I had learned. The refactored project can be found here: pay-calc-plus, and the original code can be found on the legacy branch. Additionally the earliest prototypes can be found here: simple-pay. Even though this was a toy project, I wanted to clean it up (encapsulate functional code into classes), add testing (I had 0 tests in my original program), package it better, and experiment with uv.
Staying Focused
A common mode of failure I have on personal projects is slowly losing context. After long periods of time, context
loss creates so much friction that I end up losing interest entirely.
I implemented two tactics to help combat this. Daily check-ins, and a logbook.md I
think I got these ideas from a HN comment or post I read within the past month, but I can't seem to locate
it. 😔
One thing I found that helped immensely was making a habit of checking in on the project every day. A
daily check-in. I made no expectation to write any code, or add any feature. I just spent at least 10
minutes reading through the code, and thinking about what I might do next.
Starting is often the hardest part of doing something continually. A lot of times after doing the daily check-in, I found myself getting interested and working on something minor and spent more time than I intended to.
Another thing that helped me keep the project fresh was creating a logbook.md. It served as a place to mark daily check-ins, and write informal messages about what I accomplished, or would work on next. This was critical to keeping context fresh.
# Captain's Log
2025-09-05 - clone project and start cleanup. next workthrough slowly but surely
2025-09-09 - start refactor; created MainWindow 1h; REVIEW THE DATA, THEN think about code!;
2025-09-10 - refactor load_widgets 30m;
2025-09-11 - add widgets 30m; refactor, and prototype tree 30m; add treeview 1h;
2025-09-12 - experiment w/ pytest add Paycheck 1h;
- add Deductions, take time to review code, and learn more on dataclass 1h30m;
I had A LOT OF SUCCESS with both these techniques, and have started to add a top level
logbook.md in most of my projects. Previously I would check my commit log git log -n 4
which was great for seeing written code, but not so great for remembering what I might do next, or tracking informal
progress. Plus who doesn't want to keep a "Captain's Log", it makes you feel like you're aboard the USS Enterprise
with Captain Kirk.
Testing
Normally for a toy project I would never add testing. However I've been wanting to do more test driven development, and take a design by contract approach. I already had code that worked when I started the refactor so I significantly less prototyping (which is how I primarily drive development with things that are newer to me), and instead could focus on design choices. Once I had rewritten the basic functionalities I started on adding tests with pytest.
Testing is not just good, or great. Testing is AWESOME! Previously for projects of this caliber it always seemed too tedious to be worth it, but I could not be more wrong. The joy of making a minor change and seeing all the green checks of passing tests is glorious. Alternatively if half the tests fail due to a minor typo they can be quickly found and fixed (I suspect linters could catch typos, but I admit I don't use those regularly).
Tests saved me a bunch of time in this project, and I'd like to make an effort moving forward to find ways to incorporate it more, even when I'm prototyping my way to victory.
He-Who-Must-Not-be-Named
As much as I'd love to not mention Claude I don't think I can avoid it. One of the key reasons I redid this project was to see how much I had grown as a programmer. If I relied on an llm for everything it would defeat the purpose of doing this. I used Claude for sanity checks, and to help unblock me when I knew there was a better solution, but I was unsure how to get there.
When asking Claude to review my code I found that it either confirmed a suspicion I had (and helped me feel more confident to implement the suggestion) or it would offer me a highly over engineered solution. The latter would help give me an idea of what I might want, and gave me a direction to go in.
Reflections
I had a lot of fun with this project. It's all too easy to get caught up in the day to day and feel like you aren't learning or haven't learned enough. Going back to an old project and refactoring it is a great way to combat that.
There were times I didn't want to finish working on this project. The actual program is not something I would still use (or anticipate others using), or find all that interesting. But I was really interested in creating a cleaner, robust, packaged project, and having a dirty one that already worked was the ideal starting point.
For future projects I may even have two explicit phases, one where I write it as quickly as possible and create working code, and another (a few months down the line) where I come back and clean it up. Starting with something running made improving (and applying additional concepts) SO MUCH EASIER. By nature of my interests this will likely happen automatically.
Doing daily check-ins and having the logbook.md were critical to completion. Making the
time to work towards something continually in this day and age is a real challenge (for me). However, I realize that
when I have projects to contribute to in this manner is when I am happiest. The work in itself (writing code to
solve problems, and improving on that craft) is its own reward, even when it's something I may not be all that
interested in.