What Electrical Engineering Taught Me About Building Software
I didn't start out writing software. I started with circuits: resistors, signals, control systems, and a lot of time staring at diagrams wondering why something that should work didn't. When I moved into software, I assumed most of that would be left behind. It wasn't. Some of the most useful habits I have as a software engineer came straight from electrical engineering.
Here are the ones that have stuck.
Everything is a system
In electrical engineering you learn quickly that no component exists on its own. Change one resistor and you change the current somewhere else. Add a load and the voltage drops. You can't reason about a part without reasoning about what it's connected to.
Software is exactly the same, but it's easier to forget because the connections are less visible. A small change to a shared function, a database schema, or an API response ripples outwards. Most of the painful bugs I've dealt with weren't really bugs in one place. They were surprises at the boundary between two places.
So the habit I brought over is simple: before I change something, I ask what it's connected to. Who calls this? What depends on this shape of data? What happens downstream if this is slow, or empty, or wrong?
Feedback loops decide behaviour
Control theory was one of the parts of my degree that changed how I see things. The core idea is that a system's behaviour is shaped by its feedback. Good feedback keeps it stable. Delayed feedback makes it oscillate. No feedback means it drifts.
I think about software teams and products the same way. If it takes days to find out whether a change works, people make bigger, riskier changes and correct too late. If tests run in seconds and deploys are small, you get quick, gentle corrections.
This is also the main thing I've learned about using AI coding agents. They are extremely fast at producing code, which means the feedback loop matters more, not less. If an agent can write a hundred lines in a minute and I take a day to discover they're wrong, I've made the system less stable, not more. Fast generation needs fast checking: good tests, small changes, and actually reading the diff.
Design for failure, not just for success
In hardware you don't get to assume things will behave. Components have tolerances. Power can spike. Wires come loose. So you design with margins, add protection, and think about what happens when something fails.
Software engineers often design for the happy path and treat failure as an edge case. I try not to. What happens if the network call times out? If the third-party service returns something unexpected? If a user does the thing nobody thought they'd do?
AI makes this more pressing. A language model is a component with very wide tolerances. Most of the time it does what you want. Sometimes it confidently does something else. If you build a product around a model, you have to treat its output the way an electrical engineer treats a noisy signal: validate it, bound it, and have a sensible fallback.
Measure before you guess
One of the first things you learn in a lab is that your intuition about a circuit is often wrong. You put a probe on it and find out. The scope doesn't care what you expected.
In software the equivalent is logs, metrics, and profilers. I've lost count of the times I was sure where a performance problem was and turned out to be wrong once I measured. The habit of measuring first saves a lot of time spent fixing the wrong thing.
Simple beats clever
Elegant circuits are usually simple ones. Fewer components means fewer things to fail and an easier time when you're debugging at two in the morning.
I've come to feel the same about code. Clever abstractions feel good to write and are often painful to maintain, especially for the next person. And these days the next person might be an AI agent reading your codebase for context. Clear, boring, well-named code is easier for both humans and agents to work with. That's become a practical argument for simplicity, not just an aesthetic one.
What didn't transfer
To be fair, not everything carried over. Hardware is unforgiving in a way that made me cautious, and early on I was slower to ship software than I needed to be. Software lets you change things cheaply, and part of the shift was learning to use that: ship something small, see what happens, adjust.
Getting that balance right, careful enough to build things that last but quick enough to learn, is something I'm still working on.
Why it matters more now
There's a lot of talk about AI writing most of the code. Whatever the exact numbers end up being, I think it raises the value of exactly these habits. If generating code gets cheap, the scarce skill is understanding systems: how parts connect, where feedback comes from, how things fail, and what to measure.
That's what my engineering training gave me. I didn't expect circuits to be the best preparation for working alongside AI agents, but in a lot of ways they have been.