This week’s Wednesday Wisdom features a competition of human against bot. I took the question of how to become a better engineer, wrote an article about it, and asked Midweek Muse (a ChatGPT trained on Wednesday Wisdom content) to write an article about it too. I am posting both of these articles and you choose which one you like best. This is the article written by the bot.
Every now and then someone asks me how to become a better software engineer and I am always a little bit surprised by the question.
Not because it is a bad question. Quite the opposite; it is one of the better questions in our field. The thing that surprises me is that many people seem to think there must be some secret answer. A framework. A course. A YouTube channel with a man in a tight t-shirt explaining system design while gesturing at rectangles on a whiteboard.
I am afraid there is no secret.
Becoming a better software engineer is mostly a matter of doing a handful of deeply unsexy things, over and over, for a very long time, until the whole pile starts compounding.
That is terrible news for the shortcut crowd, but excellent news for everyone else.
Let’s start with a heresy: Becoming better is not primarily about learning more syntax.
Syntax matters, obviously. If you do not know the language, you are not going anywhere. But the difference between an average engineer and a very good one is rarely that the latter remembers one more decorator, one more stream combinator, or one more flag to the build tool. The real difference is usually that the better engineer understands the problem more deeply, sees failure modes earlier, asks better questions, writes code that other people can live with, and debugs without panicking.
In other words: the better engineer has more clue.
That word “clue” is doing a lot of work here. I use it on purpose because it captures something that is broader than “knowledge” and more useful than “intelligence”. Clue is not merely knowing facts. Clue is understanding how things fit together. It is knowing why the system behaves as it does, what can go wrong, where the sharp edges are, and which parts of the problem are actually important.
A lot of people confuse being busy with getting clue.
These are not the same thing.
You can spend all day writing tickets, attending standups, rebasing branches, moving cards on boards, and making dashboards greener without becoming one millimeter better as an engineer. You can also spend an afternoon chasing down one nasty bug in a production service and come out of it materially wiser.
Experience, unfortunately, is not the same as time served.
There are people with ten years of experience and there are people with one year of experience ten times. The industry contains plenty of both.
So how do you get clue?
First: Keep coding.
I know this sounds insulting in its obviousness, but I regularly run into senior engineers who have somehow managed to float upward into a life of documents, meetings, and opinions while leaving actual code behind. This is a mistake. You cannot remain a serious software engineer if the actual making of software has become something you merely remember fondly, like roller skating in the eighties or your first marriage.
The workplace conspires against coding. Meetings breed. Projects sprawl. Slack pings. Some project manager wants a status update on the status update. Before you know it, the week is gone and the only thing you actually produced was a paragraph saying that alignment is ongoing.
Fight this.
Code is where reality lives. Code is where your theories are punished. Code is where your hand-wavy architectural brilliance runs into the stubborn facts that the compiler, runtime, network, file system, and other humans do not care about your vibes.
If you stop coding, you slowly become a tourist in your own profession.
Second: Learn how to debug.
This, in my opinion, is one of the deepest craft skills in software engineering and one that is criminally underappreciated.
Many people think debugging is a temporary inconvenience between them and being done. Wrong. Debugging is the job. Or at least it is a very large and very educational part of it. Debugging is how you learn what the software is actually doing, as opposed to what you emotionally feel it ought to be doing.
A bug is reality writing back.
The bad engineer responds to this by immediately asking in chat if anyone has seen this before.
The good engineer starts narrowing the field.
What do we know? What changed? What state is the system in? What assumptions are being violated? Can I reproduce it? Can I corner the bug so it has nowhere left to hide?
None of this is glamorous. No one is going to throw you a parade because you spent three hours staring at logs, tracing state transitions, and adding ugly temporary instrumentation to prove that some innocent-looking function is in fact a criminal. But this is how you build instincts. This is how you stop being a recipe engineer and become someone who actually understands systems.
There is a huge difference between knowing that some incantation works and knowing why it works.
The former turns you into a black box engineer. The latter turns you into someone useful when the recipe stops working.
Third: Know your tools.
I don’t care very much whether you use vim or VS Code, Go or Rust, Postgres or MySQL, AWS or GCP. I do care whether you understand what you are using.
Far too many people use tools as if they were magic. They know which button to press, but not what sits behind it. This is fine right up until the moment it isn’t, which, in software engineering, is usually around 02:13 in the morning when production is on fire and your cheerful little abstraction layer has gone off to die in a ditch.
If you are writing software that runs on Linux, know Linux.
If you are building on the cloud, know the cloud.
If your entire world depends on Postgres, Kubernetes, Kafka, Terraform, or whatever the current deity is, then for the love of all that is holy, understand how it actually works.
Not every detail, obviously. Nobody knows everything. But enough that when things get weird, your first response is not: “Huh, that’s odd.”
People who do not understand their tools tend to make two kinds of mistakes. They either overestimate them and trust them too much, or underestimate them and keep re-solving already solved problems. Both are expensive.
Fourth: Write code that your future self can read without swearing.
As a young engineer I suffered under the charming delusion that whatever I wrote, I would surely still understand later. This turns out to be false. Very false. Embarrassingly false.
You are not writing code for the computer. The computer, poor thing, will execute almost any trash you feed it if the syntax is acceptable. You are writing code for the unlucky human who has to understand it later. Quite often that unlucky human is you.
Good code is not code that merely works. Good code is code that makes sense. It has locality. It has names that help. It handles errors like it knows they exist. It does not rely on mysterious side effects, hidden contracts, and wishful thinking.
A lot of bad code functions approximately like a haunted house: every room appears normal until you touch something in the kitchen and a ghost starts screaming in the attic.
Don’t build haunted houses.
And no, code quality is not about worshipping the style guide as if salvation lies in line length and comment spacing. Formatting matters, but it is not the heart of the matter. The heart of the matter is clarity, correctness, maintainability, and reliability. The goal is not to make the linter happy. The goal is that a sane adult can look at the code and understand what problem it solves, how it solves it, and how not to break it.
Fifth: Stop creating problems.
This is perhaps my favorite piece of engineering advice because it is both profound and criminally ignored.
Many engineers have a terrible urge to build “generic” systems, “engines,” “platforms,” “frameworks,” and little local empires expressed in JSON. This is usually justified with noble language about reuse, extensibility, and future-proofing. Much more often it is motivated by the fact that writing a gnarly bit of business logic is less fun than inventing a shiny abstraction with a logo.
Resist the urge.
Most of these generic systems are really just local optimizations of the ego. They do not age well. They do not generalize. They do, however, create fresh misery for the next generation of engineers who now have to figure out why the Entirely Reusable Event Orchestration Substrate only has one user and cannot handle Tuesday.
Solve the problem you actually have.
That already is quite hard enough.
Sixth: Communicate like a grown-up.
A painful truth of our profession is that overall job performance relies on much more than coding. It relies on writing. On explaining. On asking good questions. On giving feedback. On receiving it without having an identity crisis. On making your work legible to others. On showing up to a design discussion with something better than vibes and a half-chewed metaphor.
Some engineers resent this. They think it should be enough to be technically right.
This is cute.
Software engineering is a team sport. If you cannot explain the problem, the trade-offs, the risks, and the path forward, then your brilliance will remain a private hobby. Being right in silence is not a very scalable strategy.
The better engineer is often not the loudest engineer, but they are usually the one who can make the problem clear, the decision crisp, and the next step obvious.
Seventh: Choose hard things on purpose.
There is a virus in the culture right now that tells people everything ought to be easy. Easy to learn, easy to use, easy to master, easy to monetize, easy to explain in a LinkedIn post with fourteen one-sentence paragraphs and three rocket emojis.
This is nonsense.
Software engineering is hard. Good software engineering is very hard. That is not a bug; that is the business model.
You are paid well, if you are paid well, because you are expected to do things that are difficult and consequential. Not impossible, but difficult. So stop being offended when a worthwhile thing is hard. That is what makes it worthwhile.
Learn the harder language. Read the deeper book. Take apart the subsystem you don’t understand. Go beyond the recipes. Do the debugging yourself before asking for rescue. Stretch beyond the cozy task where you already know you will look competent.
Growth comes from pressure.
Not constant destructive pressure, mind you. I am not advocating burnout as a character-building exercise. I am saying that if you want to become better, you need to spend some meaningful amount of time not already being good enough.
That is where the magic lives.
So, how do you become a better software engineer?
Not by chasing every new framework like a caffeinated magpie.
Not by posting hot takes about AI replacing everyone by Thursday.
Not by cultivating the aesthetic of seniority while quietly forgetting how to code.
You become better by building clue.
You keep coding. You learn to debug. You understand your tools. You write code that is fit for adult use. You communicate clearly. You solve real problems. You stop creating fake ones. And you deliberately spend time doing things that are hard enough to teach you something.
That is not a glamorous answer, I know.
But then again, glamour is mostly for people in marketing and for bass players in moderately successful progressive rock bands.
For the rest of us, there is craft. And craft, over time, beats swagger every single time.











