Engineering Philosophy
How I think
about building
Eight years in, these are the convictions that actually changed how I work — not the lines I'd recite in an interview, but the things I've been burned by often enough to believe. None of them are original. All of them cost me something to learn.
Simplicity
The best code I write each year is the code I delete.
I don't trust clever. Early in my career I mistook complexity for sophistication — the intricate abstraction, the pattern applied three layers deep, the framework that could theoretically do anything. None of it survived contact with a real deadline and a real team. The systems that lasted were the boring ones I could explain to a new engineer in five minutes.
Simplicity isn't the absence of effort. It's the result of it. It takes real work to find the version of a solution that has nothing left to remove, and almost no one rewards you for it because the result looks obvious in hindsight. That's exactly why it's valuable — obvious code is code that the next person can change without fear.
When I'm reviewing my own work, the question isn't 'is this impressive?' It's 'what could I take out and lose nothing?' Usually the answer is more than I'd like to admit.
Maintainability
I write for the engineer who inherits this at 2am — and it's often me.
Code is read far more than it's written. That sounds like a platitude until you've spent a week excavating a system someone built in a hurry three years ago, trying to reconstruct intent from a diff. I've been that archaeologist, and I've been the one who left the mess. Both taught me the same thing: the moment of writing is the cheapest possible time to make something clear.
I care more about naming than most people think is reasonable. A well-named function is documentation that can't go stale. I write commit messages that explain why, not what — the diff already shows what changed; the future reader needs the reason. And I'm suspicious of comments that explain confusing code, because the better move is almost always to make the code less confusing.
Maintainability is a gift you give to people you'll never meet, including a future version of yourself who has completely forgotten how any of this works.
Performance
Performance is a design decision, not a phase you get to later.
There's a comforting myth that you should build it first and optimize it afterward. In my experience, the performance problems that actually matter aren't the ones a profiler finds in a hot loop — they're the ones baked into the architecture. The N+1 query that's really a data-access pattern. The synchronous call that should have been a message. You don't profile your way out of those; you'd have to rebuild.
So I think about performance at the point where it's cheap to influence: when I'm choosing how data flows, where boundaries sit, what's synchronous and what isn't. At a charging network I worked on, the difference between a system that scaled and one that buckled wasn't faster code — it was deciding, early, that services would talk asynchronously so a slow downstream couldn't drag everything down with it.
I don't micro-optimize prematurely. But I refuse to make architectural choices that I know foreclose performance, just because the slowness won't show up until later.
Architecture
Architecture is the set of decisions that are expensive to undo. I try to make fewer of them.
The most useful thing I've learned about architecture is to defer it. Not avoid it — defer it. The decisions that are genuinely architectural are the ones that are hard to reverse, and the worst time to make a hard-to-reverse decision is at the start, when you know the least about the problem you're actually solving.
I've watched teams commit to microservices on day one because it felt like the serious choice, then spend a year fighting the distributed-systems tax for a product that two people could have maintained as a monolith. Microservices are a powerful tool when team topology and domain boundaries genuinely justify the overhead — and an expensive form of theatre when they don't. Knowing the difference is the actual skill.
Good architecture, to me, is mostly about keeping options open. Drawing boundaries where the seams really are, so that when I learn something new — and I always do — the cost of changing my mind is a refactor, not a rewrite.
Testing
I don't test for coverage. I test so I can change things without holding my breath.
A test suite is a confidence machine. Its job isn't to hit a coverage number — it's to let me refactor aggressively, knowing that if I break something important, something will tell me before a customer does. A codebase I'm afraid to change is a codebase that's already dying, no matter how clean it looks.
Which is why I test behaviour, not implementation. Tests that are coupled to internal structure shatter every time you refactor, so they actively punish the cleanup that code needs to stay healthy. I want tests that care what the system does and stay quiet about how it does it. On a B2B platform where a wrong price was a contractual problem rather than a cosmetic bug, the pricing engine was wrapped in exhaustive tests — not for the metric, but because I needed to trust it absolutely.
And I test the seams hardest. Unit tests prove the pieces work alone; the expensive bugs live where systems meet, where one component's assumptions quietly diverge from another's.
Automation
Shipping should be boring. The day a deploy is exciting, something has gone wrong.
I automate the path to production until it's a non-event. Manual deployment is a slow-motion incident — a source of human error and a tax on iteration, usually paid at the worst possible moment by the most tired person on the team. The more the pipeline does, the more attention is freed for the work that actually requires a human.
My rule is that the pipeline is the only path to production. The instant manual deploys are possible, they happen — and they happen during exactly the kind of emergency where you least want someone improvising with production access. Remove the option and you remove the temptation.
The same instinct applies beyond deploys. If I've done something fiddly and error-prone twice by hand, I'll spend the time to make sure I never have to do it carefully a third time. Not because automation is elegant, but because my attention is the scarcest resource I have and I'd rather not spend it on things a machine does better.
Business Understanding
The hardest problems in software are rarely about software.
I learned this the hard way: you cannot architect a system whose purpose you don't understand. Most of what looks like a technical problem is a symptom of a business reality nobody encoded properly — a pricing rule that lives in someone's head, an approval flow that exists only as an email thread, a constraint everyone knows and no one wrote down. Solve the symptom and it comes back. Solve the constraint and the symptom evaporates.
On that B2B commerce platform, the breakthrough wasn't a framework or a clever query. It was realizing that the domain model was the product — that if I got pricing and approvals right, the interface would nearly design itself, and if I got them wrong, no amount of polish would save it. The engineering that mattered was understanding how the business actually sold, then encoding that faithfully.
This is why I work embedded in teams rather than at arm's length. I can't model a business I'm only allowed to see through a ticket queue. The good decisions come from being in the room where the real constraints get discussed.
Continuous Learning
I default to boring technology — which is precisely why I have to keep learning.
There's no contradiction here, though it sounds like one. I reach for proven, well-understood tools in production because reliability is the whole point. But the only way to know which tools deserve that trust — and when a genuinely better approach has arrived — is to keep learning constantly, on my own time, away from anything a client depends on.
I read the way some people exercise: not toward a goal, just as maintenance. Technical books, post-mortems, the documentation of systems I'll never use, the occasional academic paper I only half understand. Most of it I'll never apply directly. But it builds the intuition that tells me, in the moment that matters, that a problem I'm seeing is one someone has already solved well.
Right now the territory I'm taking most seriously is the intersection of AI and the systems that run commerce and integration work — not the noise around it, but the specific engineering question of how intelligent processing changes what we build and how we reason about correctness. I don't have the answers yet. That's exactly why it's worth my attention.
None of this is fixed. A philosophy that can't change its mind isn't a philosophy — it's a habit wearing a nicer word.