Skip to main content
  1. Posts/

Things I Changed My Mind On

·1353 words·7 mins
Photograph By ThisisEngineering - Unsplash
Blog Software Engineering
Table of Contents

A Living Document
#

I read Chris Kiehl’s post about software engineering opinions he changed his mind on after 6 years in the industry, and it hit different. Not because his takes were revolutionary, but because I recognized myself in the “before” column of almost every one. So I figured I’d write my own version. Not after 6 years — I haven’t been doing this that long — but long enough that some opinions I held tightly have quietly loosened their grip.

This isn’t a “lessons learned” post. It’s more like a changelog for my brain.

If It’s Simple and You Do It Often, Script It
#

This one came from my systems engineering days, not software engineering. When you’re SSH-ing into servers at 2 AM because something broke, you learn very quickly that anything repeatable should be automated. Not “could be” automated. Should be. The third time you run the same 7 commands in sequence, you’ve already wasted two times’ worth of effort.

I carried this into software engineering and honestly, it’s the one belief that hasn’t changed — it just got more sophisticated. What used to be bash scripts became CI pipelines. What used to be “restart the service” became infrastructure as code. The principle is the same: if you’re doing it by hand and it’s predictable, you’re making a mistake.

Documentation Is Not a Chore
#

I used to treat documentation like flossing — I knew I should do it, I told other people to do it, and I almost never did it myself. That changed when I started using Obsidian as a personal knowledge base.

The shift wasn’t about writing docs for other people (though that matters too). It was about writing docs for future me. I work with multiple technologies across different projects, and my brain does not have the capacity to hold all of it. Having a searchable vault of notes means I can pick up a technology I haven’t touched in months and get back up to speed in minutes instead of hours.

At work, I started advocating for documentation after watching new team members struggle with the same things I struggled with — things that could’ve been a 5-minute read instead of a 2-hour archaeology expedition through Slack messages and git blame. Documentation isn’t a chore. It’s a multiplier. You write it once, and it pays dividends every time someone (including you) doesn’t have to reverse-engineer the same context.

The Right Tests > All the Tests
#

Before our load testing adventure , my relationship with testing was… academic. I knew tests were important. I wrote unit tests because that’s what good engineers do. I mocked dependencies because that’s what the testing guides said. And then our production app started choking under load, and exactly zero of those unit tests had anything useful to tell us.

What actually saved us was a load testing script. Not a sophisticated testing framework. Not 90% code coverage. A script that threw traffic at our APIs and showed us where things fell apart. That’s when my opinion shifted from “tests are important” to “the right tests are critical.” A bunch of unit tests with mocked databases don’t tell you much about how your system behaves when 200 users hit the same endpoint simultaneously. Sometimes the most valuable test is the one that simulates reality.

I still write unit tests. But I spend a lot more time thinking about what to test now, rather than trying to test everything.

AI: Useful Copilot, Terrible Autopilot
#

My take on AI tools has gone through a few phases. First it was excitement — “this thing writes code for me!” Then mild concern — “wait, is this thing going to replace me?” Then the current position, which is more nuanced: AI is genuinely useful, but you need to be the one driving.

I have Copilot configured in NeoVim but disabled by default. I use Cursor when I need heavier AI assistance. And what I’ve noticed is that AI works best when I already know what I want and roughly how to build it. It’s a fantastic autocomplete for intent. But when I’ve tried to let it lead on something I don’t understand? That’s when the bugs creep in, the architecture gets weird, and I spend more time debugging AI output than I would’ve spent just learning the thing myself.

Here’s my rule: if you can’t do it yourself, you probably shouldn’t be using AI to do it. Not because AI can’t produce the code — it can. But because you won’t be able to evaluate whether that code is any good. And shipping code you can’t evaluate is how you end up with production incidents that nobody on the team can debug.

Tooling Is Preference, Not Personality
#

I spent an embarrassing amount of time early on agonizing over tools. Which editor. Which terminal. Which Linux distro (I went through a few ). Which framework. Which database. And while I still think it’s valuable to explore — trying NeoVim taught me a lot about how I work, even if it hasn’t made me a better engineer per se — I’ve come to accept that most of it is preference.

If VS Code gets the job done for you, use VS Code. If you love IntelliJ, great. The person next to you using a different editor isn’t doing it wrong, they’re just doing it differently. Spend some time trying other tools when you can, but don’t obsess over every new release. The tool is not the craft.

Over-Engineering Will Haunt You
#

This one I learned the hard way. There’s a specific kind of pain that comes from opening a codebase you wrote 6 months ago and realizing you built an abstraction layer for a problem that never materialized. The factory pattern for a thing that only ever has one implementation. The config system that supports 15 environments when you only deploy to 2. The generic utility function that handles 8 edge cases, 7 of which have never occurred.

Every line of code is a liability. Every abstraction is a bet that the complexity it introduces will be justified by future use. And most of those bets don’t pay off. I’m a lot more comfortable with a little duplication now than I used to be. Three similar functions that are each 10 lines long and obvious are better than one 50-line function that handles everything through a maze of parameters and conditionals.

Rules Are Made to Be Understood (Then Occasionally Broken)
#

A good engineer follows engineering principles. A great engineer understands that any rule can — and should — be broken given the right circumstances. The trick is knowing the difference between “I’m breaking this rule because I understand the tradeoff” and “I’m breaking this rule because I’m lazy.”

SOLID, DRY, YAGNI — these are useful frameworks, not commandments. When someone tells me “we can’t do it that way because it violates [principle],” my follow-up is always “okay, but what’s the actual consequence?” Sometimes the answer is “bugs and maintenance hell.” Fair enough, follow the principle. Sometimes the answer is “well, it just… isn’t how you’re supposed to do it.” That’s when you push back.

Know When to Stop Digging
#

Everything is a learning experience. I genuinely believe that. But time is a finite resource, and knowing when you’ve gone “deep enough” on something is a skill I’m still developing. Not every rabbit hole leads to treasure. Sometimes it’s just a deeper hole.

I’ve spent weekends configuring NeoVim plugins that I used twice. I’ve gone deep into Kubernetes networking concepts for clusters I’ll never manage. I’ve read entire documentation sites for libraries I decided not to use. Was it all worthless? No — some of it connected dots I didn’t expect. But some of it was just procrastination disguised as learning.

The honest answer is: I don’t have a clean rule for this yet. But I’m getting better at asking “will this knowledge compound?” before diving in. If the answer is “probably not,” I give myself permission to skim and move on.

Aaron Yong
Author
Aaron Yong
Building things for the web. Writing about development, Linux, cloud, and everything in between.

Related

Living in the Terminal
·1145 words·6 mins
Photograph By Oleksandr Chumak
Blog Terminal Productivity
How TUIs replaced my GUI tools and accidentally made my workflow more accessible
Load testing with Grafana K6
·1414 words·7 mins
Photograph By National Cancer Institute
Blog Testing Node.js
Our first foray into load testing our backend APIs
Logging Google App Engine Applications
·1097 words·6 mins
Photograph By Kelly Sikkema
Blog Google Cloud App Engine Node.js
How to add logging to your Google App Engine application