I'm Back on Java Spring Boot (And Yeah, It's Kind of a Gem)
Went from TypeScript everywhere to full OOP mode. Java is verbose as hell, Spring Boot makes you write a folder structure before you write a feature, and somehow I'm not mad about it.
Back in Java. Full OOP mode. @Autowired everywhere, interfaces for classes that will only ever have one implementation, a UserServiceImpl because apparently the language is allergic to just calling it UserService.
And I kind of love it.
Okay But I Should Admit Something First
I touched Java in college, first and second year. Doesn’t mean I actually learned OOP back then. Deep ChatGPT era, half the class pasting errors into a chatbox instead of reading them.
Professor mentioned the four pillars without ever drilling them. Two years of “Java experience” and I could write a class. Couldn’t tell you why it should exist the way it did.
TypeScript, Node, Express taught me to write decent code, just not through OOP. DRY, KISS, YAGNI, small functions, no copy-pasted logic, all absorbed on my own from real projects, not a lecture.
SOLID’s the one that never landed. Express is mostly async handlers and middleware chains, you can write genuinely good Express code without ever touching an interface. Java forces “program to an interface” structurally, whether you want it or not.
Wait, You Actually Like This?
Coming from a backend that fits in one file if you’re feeling spicy, Java’s a different universe. Controller calls Service calls Repository calls Entity, each one its own file. First stretch back felt like paperwork just to say “get user by id.”
But once the ceremony’s done, the code doesn’t lie to you. Spring’s DI container wires everything at startup, the compiler yells before your users do.
@Servicepublic class RecordMigrationService { private final RecordRepository recordRepository;
@Transactional public MigrationResult migrate(LegacyRecord legacy) { var record = RecordMapper.fromLegacy(legacy); recordRepository.save(record); return MigrationResult.success(record.getId()); }}Nothing clever here, on purpose. Constructor injection, one transaction boundary, one job. Boring code that does one thing is the whole appeal once you’ve been burned by code that tries to do five.
OOP, Actually Explained This Time
Nobody explained why the four pillars matter until I lived inside a real Spring codebase. Turns out the pillars aren’t the point, they’re the mechanism. The actual point: a change to one part shouldn’t force you to touch every other part.
First time I needed a mocked repository, swapping it in was a one-line config change. That’s when the concept stopped being a definition and became something I actually reach for.
Composition over inheritance clicks once you’ve been burned. I modeled two record types extending a shared base class, seemed clean, they shared fields. One needed a validation rule the other didn’t, and I was shoving type-specific logic into a shared parent to dodge it.
Switched to composition instead, both types hold a shared metadata object rather than extending a common parent. Inheritance means “is fundamentally the same kind of thing as.” Sharing a few fields isn’t that claim.
The OOP Shitss, Defended
I get the meme. AbstractSingletonProxyFactoryBean is a real class name in the Spring source. Not defending that one.
You can drop into any one layer without holding the whole system in your head. Matters more the longer a codebase outlives whoever wrote it first.
What I’d Tell Someone Starting From Zero on This
If your OOP education was also a professor who skipped it, here’s the honest version nobody gave me: the ceremony isn’t there to make you feel productive. It’s there so “productive” doesn’t quietly turn into “fragile.”
Learn @Transactional before any framework trick that looks good on a resume. Understand what a transaction boundary actually is, not just where the annotation goes. You’ll catch a whole category of bug before it ships.
Don’t skip the Service layer because an endpoint feels “simple enough.” Every “simple” endpoint eventually grows a business rule, and every one I skipped became a small refactor later. Cheap lesson small, expensive big.
And look at the actual SQL your ORM generates, at least once. Trusting the abstraction blindly is how a dumb N+1 query slips through local testing on twelve rows and only shows up under real load.
What Actually Annoyed Me
Checked exceptions on methods that’ll never realistically throw. Compile times worth a coffee break on a big module. Boilerplate real enough that Lombok exists purely to generate getters and setters.
None of it’s fatal. All of it’s a tax for the predictability, and most days that trade’s worth it.
The Honest Take
Not trendy, won’t show up in a “cool new stack” thread. But it produces code that still works the same way in three years when someone else owns it.
There’s a gap between “fun to write” and “fun to maintain,” and most of my early projects chased the first one. Java optimizes hard for the second. Verbose, yeah, no argument, wiser than I gave it credit for.
Conversation
Discussion
Comments will appear here after GitHub Discussions and Giscus are configured for this repository.