Andreas Fragner Andreas Fragner

The siren call of wrong-way dependencies

Dependencies that point in the wrong direction are one of the easiest and most costly design mistakes one can make.

Dependencies that point in the wrong direction are one of the easiest and most costly design mistakes one can make. Easy because they’re often the path of least resistance. Costly because they make it harder to make changes.

What’s a wrong-way dependency? Higher level, more abstract components that depend on lower level, more concrete ones. Dependencies should be inwards pointing, in the sense that the more fundamental parts of your system sit on the inside and the implementation details on the outside [1].

Introducing such wrong-way dependencies is extremely easy and I see it done all the time, even by experienced engineers. They give you short-term gain for long-term pain.

Consider the following example: Suppose your system interfaces with a bunch of different third-party service providers which add different functionality to your product (say, different payment gateways for different payment methods). Further, suppose that your customers need to be registered in advance with each provider prior to being able to use the functionality offered by it. For any given customer, you need to be able to tell at runtime whether the functionality is available to them (e.g. they can use a given payment method). There are two ways to address this:

  1. Add a boolean property to your customer model for each provider that indicates whether a customer has been registered with that provider, defaulting to false.

  2. Add a way to query each service provider for whether a customer has been registered with it. The provider’s API might support this directly, or if not we’d wrap it and maintain a list of registered customers ourselves.

Option 1 is tempting because it involves less work at the outset. But now suppose we want to make any of the following changes:

  • Introduce a new service provider

  • Change the identifier or reference for a service provider

  • Add more fine-grained permissions

All of these now entail touching customer code or data, even though they have no bearing on the core logic for managing customers. Changes are now more costly and riskier. Option 1 was faster to implement but has increased the blast radius of changes significantly.

How do you avoid wrong-way dependencies? Think about what kinds of product changes you will need to make in the future, then avoid dependencies that make them difficult. Of course it’s hard to predict how requirements might change, but the big axes are usually fairly obvious. Avoid speculation and focus on the obvious ones.


[1] Bob Martin makes this point in many ways in Clean Architecture. “Depend in the direction of stability” is a particularly succinct one.

Read More
Andreas Fragner Andreas Fragner

It’s hard to grow without trust

Trust is a key enabler of growth (and conversely, lack of trust a key inhibitor).

Ben Kuhn makes a great observation in his latest post: Trust is a key enabler of growth (and conversely, lack of trust a key inhibitor). If you don’t trust someone’s work, you’re going to spend extra effort double checking their output — reviewing their designs, second-guessing their technical decisions, fine-combing their PRs, etc. This can dramatically impact bandwidth and output of your team. The worst case occurs when trust is low and the cost of verifying correctness is high. People will just default to rolling their own solutions in that case instead of building on top of other people’s work, resulting in lots of duplicated effort. Lack of trust is also why the true cost of a bad hire far exceeds the nominal value of their salary and the opportunity cost of the hiring decision — they consume more bandwidth than they add because their work can’t be trusted.

How do you build trust? There’s no shortcut — trust must be earned. To become a trusted engineer or operator, you need to:

  • Consistently produce high quality work

  • Pay attention to details, think about edge cases, anticipate failure modes

  • Be highly responsive and take the initiative — be the person on support rota that makes everyone sleep well at night

  • Be serious about reviewing other people’s work and give detailed, actionable feedback

I think the last point is generally under appreciated. The most effective engineers and operators are not just good ICs but actively work to build trust in others by helping them improve. They want to be trusted, but they also want to be able to trust others because they know it will make the whole team more productive. A high degree of ownership ties in directly with this. If you take ownership of your team’s product/system, you’ll spend more time on quality control and ensuring changes to it can be trusted.

Read More
Andreas Fragner Andreas Fragner

Care about how the work is done

High performers care deeply not just about doing the work but how it’s done.

High performance can manifest itself in different ways and it’s important to recognize that. But one thing that all high performers I know do is to approach their work with a kind of metacognition: They care deeply not just about doing the work but how it’s done. They obsess about efficiency — from small details (keyboard shortcuts, editor configuration, etc.) to general strategies (how to structure their day, how to minimize distractions, knowing when they’re at peak performance, etc.). They understand the power of iteration and that you can only get better if you constantly examine and revise your own work. Great engineers, for example, have a habit of reviewing their own PRs because it gives them a top-down view of their output and a chance to see whether they could’ve solved the problem differently.

Importantly, high performers take a long-term view in their pursuit of efficiency. They view their work as a craft to be refined over years, not something that has a definitive end state. This long-term orientation means they tend to strike a better balance between exploration and exploitation. Sometimes it makes sense to take the longer route because you get to learn a new technique that will speed you up in the future. The dividends of such calibrated detours often materialize more quickly than people think, but the pressure to use what you already know to ship something now can be immense.


People often brush the kinds of details high performers care about aside as not mattering in the bigger scheme of things. I’d argue these people don’t understand how high performance comes about. It’s the accumulation of a hundred micro optimizations that add up logarithmically. Doing any one thing won’t make a difference on its own, but combined they compound and get you from 70th to 99th percentile. Better keyboard shortcuts and a clean desk for example won’t make you magically produce outstanding work but they reduce context switching and keep you in flow state, which is essential to solving hard problems.

Because people tend to misunderstand how high performance is achieved, its markers are also not tested for during interviews. Looking at things like typing speed and how quickly someone moves between their editor and browser is often ignored in interviews, but in my experience, can give useful signal. Plus the nice thing about these aspects is that you can glean them passively by observation. No need to come up with tricky questions to uncover them.


So, if you care about performing at the highest level, analyze your own output and pay careful attention to how you do things. And just to state the obvious: Don’t mistake the map for the territory. Obsessing about efficiency and self-improvement are markers of high performance but they are not the end goals. Know what you want to achieve first, then work on getting there faster.


Notes:

Obsessing about efficiency is something that high-performing knowledge workers have in common with top athletes. Details matter. Practice and constant tweaking get you there. But it happens to be a lot harder to recognize that this is true for knowledge work compared to sports. I suspect this might be because performance is harder to measure and there is no direct, one-to-one competition involved. But while “winning” may not be as clearly defined for knowledge work, approaching it with a similar rigor as an athlete can still lead to dramatically different outcomes over the course of a career — regardless of whether measured in wealth, happiness, impact or whatever else matters to you.

Read More