When in Rome: Predictability, Surprise, and Software Engineering

Brian Kotos

December 20, 2025

One of the most expensive mistakes I've made as a software engineer wasn't choosing the wrong tool. It was choosing a tool --or pattern or practice-- that I genuinely believed in, and applying it in a place where it didn't belong. That mistake taught me something I now treat as a core engineering principle: surprise is a liability.

We usually hear the Principle of Least Astonishment framed as a UX rule, but it has a big impact in code. Surprising codebases take longer to understand, require more explanation, and make engineers hesitant to change things because they're unsure what else might break. That hesitation isn't a people problem, it's a design problem. Engineers build mental models, and when systems behave the way those models expect, progress is fast. When they don't, everything slows down.

This is where "When in Rome, do as the Romans do" becomes real engineering advice. Every ecosystem has norms. React and Node expect one style of code; PHP and C# expect another. Some teams care deeply about git history; others care only about what lands in main. None of these are universal truths, they're local expectations that reduce friction and cognitive load. Problems arise when something that worked well in one context is treated as a general law and applied wholesale somewhere else.

I've made this mistake myself. A few years ago, I joined a team working in a React and Node codebase with little to no test coverage. Coming from environments where heavy OOP, dependency injection, and inversion of control were the norm and where those patterns genuinely worked, I pushed hard to introduce them here, largely in the name of "testability." On paper, it made sense. In reality, it was a poor fit. The patterns felt foreign to the ecosystem, surprising to the engineers working in it, and harder to reason about than the problems they were meant to solve. I wasn't wrong about DI or OOP being useful tools, but I was wrong to treat them as universal truths. I had ignored Rome while standing squarely in it, and the project paid the price. That experience stuck with me because it forced me to confront something uncomfortable: being confident and being correct aren't the same as context changes.

That experience changed how I think about standards too. Good standards aren't about control, they encode shared expectations so engineers don't have to renegotiate the basics every day. When standards align with what people already expect, they fade into the background and let work flow. Problems start when standards are applied without regard for context.

These days, I care less about whether a solution is the "best" in the abstract and more about whether it's predictable, legible, and aligned with its environment. Engineering maturity isn't about accumulating answers, it's about knowing when not to reuse them. Brilliance might impress in isolation, but predictability is what actually scales teams.