Ask It Why
Day 25. Week six. Today the assignment was a car shop manager — a Car class, a terminal menu, the whole loop of add, view, service, update mileage, quit. Simple on paper. Not simple in practice, because this is the second day I've ever touched object-oriented programming.
Cars, Classes, and Cross-Examination
I had Gemini generate a working solution first. Not because I wanted the answer, but because I needed the shape of the thing in my hands before I could reason about it — same instinct as yesterday. Then I did the part that actually teaches me something: I went line by line, rewrote it, and asked why.
Why the underscore in front of every instance attribute? Convention — it tells the next reader "this is internal, go through a method, don't touch it directly." Why does _id come from Car.total_cars instead of getting passed in as an argument? Because the moment a human is responsible for tracking unique IDs, a human eventually repeats one. Let the class own that job and the bug class disappears. Why does _services start as an empty list instead of a constructor parameter? Because a car that just entered the system hasn't been serviced yet — the class can supply that default itself instead of making every caller remember to pass [].
The one that actually surprised me was Car.all_cars[self._id] = self. I'd never seen an object assign itself as a dictionary value before. It clicked once I understood that self inside __init__ is just a reference to the block of memory holding this specific car — and a dictionary will hold any value, including a live object. So the car registers itself into the system the instant it's created, and looking one up later is an instant key lookup instead of a scan through a list. Self-registration plus O(1) lookup, for free, from one line.
I also got the instance/class/static distinction to finally land, using my own novel's characters instead of a generic Person example. An instance method is one operative acting on themselves — self. A class method is headquarters, acting on the roster as a whole or minting new instances — cls. A static method is a reference manual bolted onto the class that doesn't touch either. Once I had a story to hang it on instead of an abstract rule, it stopped being trivia and started being obvious.
And then I got a live demo of why try/except exists at all, past the "catch errors" one-liner. I split my file into car_management.py and main.py and immediately hit ModuleNotFoundError: No module named 'car_management.py'. Import statements drop the .py. Then, once that was fixed, a NameError — I'd renamed the class to CarManager on import but was still calling Car() everywhere in the menu logic. Small, dumb, exactly the kind of thing that happens when you're moving fast across a file boundary for the first time.
One Rule to Start With
I asked for reading recommendations on class design and immediately got buried — SOLID principles, Fluent Python, ArjanCodes on code smells. All good, all too much at once. So I picked one thing to actually carry forward: the Single Responsibility Principle. A class has one job and one reason to change. That's the entire reason Car only knows about car data and main() owns the terminal menu — they'd fail for different reasons, so they don't belong in the same place.
I also started Practical Object-Oriented Design on a recommendation from the instructors, and the reframe in it is already doing work on me: stop asking what an object is, start asking what messages it sends to other objects. It's the same instinct I already use for fiction without noticing — a manuscript is a map of a story, code is a map of a machine.
The Tax on Every Exercise
The car manager itself probably took two and a half hours, and I'd already generated a reference solution before I started rewriting it myself. Part of that is genuinely learning OOP from a standing start. The other part is that every exercise right now carries a tax on top of the exercise: branch it, push it, dockerize it, wire a GitHub Actions check before it's allowed to merge. None of that is wasted practice — it's exactly the muscle memory a working developer needs — but there's a real cost to paying that tax on a toy app five times a week while also learning the underlying syntax for the first time. Something has to give, and for me that's leaning harder on AI for orientation so I can spend my actual attention on the syntax and the debugging, not on staring at a blank file wondering where to start.
Roll Call
The other shift today: attendance got tighter. Roll call, accountability checks, "where were you" energy. I recognize the shape of it from the Academy. It's what a training environment does once people start getting casual. It just means the day has a different texture now: less casual, more watched, on top of everything else already stacked on the schedule.
Day 25 was the first day OOP stopped being new vocabulary and started being a thing I could interrogate. Generating the code was the easy part. Asking why the underscore, why the dictionary, why the try block, is where the actual learning happened — and it's a habit I plan to keep running on every exercise from here, not just this one.