Stephen Kiers

Stephen Kiers

Staff+ Software Engineer

I build platforms, wrangle data, and try to make systems that don't page people at 3am. I've been doing this long enough to have opinions and scars to back them up. More about me →

Recent Writings

View all writings →

Guiding Principles

I believe these strongly, but I hold them loosely. The moment a principle becomes dogma, it stops being useful.

Keep it simple

"How can we do the boring solution?" That's the question I keep coming back to. Simple doesn't sound impressive. It's harder to write, harder to sell, and it won't get you promoted. But simple is what survives handoffs, what survives your own future confusion, and what lets you actually explain why you made a decision.

Wait for the third case

I used to abstract immediately. First sign of duplication, I'd pull it into a util. Now I know what happens: nobody discovers it, it rots, and someone writes a second version anyway. I paste in place until I've seen three cases. Then I know what I'm actually abstracting.

Measure first

Data doesn't guarantee good decisions, but it makes bad ones harder to hide. I'm not a data scientist—I'm better when I'm working with people who are and who'll call me out when I'm reading the numbers wrong.

If it's not a lint rule, it's not a rule

Style guides that live in wikis don't get followed. Warnings that scroll past in a sea of yellow don't get noticed. If something matters, automate it. If it doesn't matter enough to automate, stop pretending it matters.

Dogfood your own work

Use what you build. At Shopify, we used our own experimentation platform for feature flags—when ExP was slow or buggy, we felt it immediately. When I was doing wedding photography, I used Wedimage to deliver to my own clients—and immediately noticed things I'd missed, like not wanting to send couples there in case they browsed and picked a different photographer. That's not feedback you get from a bug report. With my audio capture side project, I catch UX issues, bugs, and missing functionality just by using it daily. Repeated use also surfaces flaky code that passes tests but fails in the real world. It's not always possible to dogfood, but when you can, do.

YAGNI

I've over-engineered features nobody asked for. I've seen companies build infrastructure for millions of users who never showed up. Build for today's requirements. The future you're predicting probably isn't coming.

Strong types

Types catch bugs in disconnected places. Types create contracts between domains. Types make refactoring possible. At Shopify, I added strong types to a Ruby codebase using early ChatGPT to convert TypeScript definitions to Sorbet. The types caught bugs we'd have shipped.

Clean code

Code that needs comments is usually code that could be written more clearly. Not always—sometimes you're explaining why, not what—but a wall of comments is a smell, not a solution. Small functions, clear names, obvious intent. I'm not dogmatic about it. But teams that align on this move faster.

SOLID (the principles, not the dogma)

I'm mostly a functional programmer, but these still apply. Single responsibility keeps things testable. Open/closed prevents bugs. I didn't understand Liskov substitution for years—it rarely comes up, but it shapes how I think about hierarchies. These aren't commandments; they're tools.

Domain-driven design (the pragmatic version)

I don't follow DDD strictly. But shared language matters—when stakeholders, engineers, and code all use the same words for the same things, confusion drops. Model the domain clearly. Contain it properly. Talk about it consistently.

Don't embarrass yourself

Before I ship something or stake my name to a position, I ask: will I be embarrassed by this later? Not "is this perfect"—I make mistakes constantly. But is this something I can defend? Did I think it through? It's a low bar, but it's the one that actually stops me from cutting corners. Code, opinions, architecture decisions—if I wouldn't want to explain it in six months, I don't ship it.

Projects