~ $ cat blog/everything-is-a-trade-off.md

everything is a trade-off

if you ask an experienced engineer any design question, probably the first words out of their mouth are “it depends”. that’s not a dodge, they’re doing their job. the value isn’t in knowing the answer, but in knowing which variables affect the decision and what’s currently at stake.

the cost is never zero

every choice you make buys you something, but charges you for it somewhere else.

  • microservices give you modularity and team autonomy, but bring in operational complexity and harder tracing across systems
  • an external auth provider speeds your time to market, but couples you to another team and their decision-making

the job of the engineer is to make those trade-offs explicit before they hit you by surprise.

the biggest failure you should avoid is being surprised by costs you could have known beforehand.

how i go about things

when i’m facing a trade-off that is not immediately obvious, i think of it as follows:

first, is it a two-way door (easy to change), or a one-way door (irreversible)?

if the cost of analyzing and deciding is higher than the cost of trying and replacing it later, simply pick the cheapest option and see how it works.

now, if it’s a one-way door, ask these for each option:

  • what does it optimize for?
    every option almost always stands best for a specific variable.

    in the case of the external-auth provider: it optimizes for speed

  • what is the cost of it?
    does the cost live in the critical path of the mission we’re trying to solve?

    it will cost us coupling to a team, and we will lose freedom on the auth methods we can use

  • what would have to be true for the other option to be a clear win?
    helps you understand what is the fundamental value you are getting from this option.

    if we knew that the provider will not implement account-abstraction anytime before 12 months, and our engineers have the ability to do so in 2 months, we would go with on-premise solution

the first two set the stakes, the last one is what helps you solve the disagreement.

following the example, we can see that we are clearly optimizing for speed, at the cost of creating this dependency, because at business level we have accepted that the coupling and rigidity it creates is justified by the time it saves us.

time is always the issue

important to note: some decisions start being easy to change and slowly become irreversible with time. pay attention to this when deciding to settle on something. time is always a variable.

i.e. a logging library is a two-way door initially, easily set behind an abstraction, but it becomes a one-way door after a year when the codebase has coupled to the implicit features of the library.

when we decouple complexity with abstraction, we need to set a timeframe to revisit the approach. if we don’t revisit the solution by that time, the door is already closing, because the code around grows to expect what’s behind.

this exists everywhere: logging, ORM, storage, auth provider; anywhere you ever thought: we can swap it later.

sometimes the most dangerous decisions are the ones we take the easiest, because we fail to account for time.

simplicity is a position, not a default

“keep it simple” is good advice, but it quietly introduces a trade-off of its own. simple for who? for the reader, the writer, or the editor? for me today, for me in one year, or for the user?

simple-to-write code is often hard-to-read. simple-to-extend abstractions are often complex-to-design.

so, it depends on what?

next time you say “it depends” don’t stop there, finish the sentence, mention or search for the variables.
that bit extra is the difference between a shrug and an engineering mindset.

x