no is such a boring word

To get something out of the way - yes, I do say no quite often. But even if "no" is in itself a complete sentence (true!), I still think it's a really boring sentence. Make better sentences.

If conversations are two-way streets, no is like a stop sign. There's no opportunity in no, no next steps, no learning, just an end to moving ideas around. No is for people who know it all.

I don't think this word has a place in groups that want to achieve a joint goal in the best way possible. No, I actually don't believe it has a place. And I can even explain why that is.

Great teams are places to share ideas, and to jointly find and give opportunities to improve. So if someone shares a shit idea with you, sure, go ahead and say no. But there's no teachable moment in shutting people up. There might be a moment of feeling smart – very ephemeral. Reality is that most folks don't come around presenting shit ideas, there's a deeper reason for that. And understanding that reasoning, understanding why and how people make decisions, their reasoning, that's a good point to speak about. But you will not if you shut people up, if you're a human-faced stop sign. So don't be.

What's better? The first thing is to assume positive intent. To be fair, up to a certain point - there's always douchebags. But assume that the overwhelming majority of people communicate with good intentions - intentions of being constructive, helpful or just sharing a great idea.

Since I'm usually assuming that the people I'm working with are neither dumb nor evil, what feels like a not awesome thought for me in the beginning might just be plain ignorance. So you go and ask until you truly understand not only what someone is sharing with you, but also the context around it. And that might be anything from individual experience (I built something like that before), environmental particularities (time pressure) or implicit assumptions that no one really knows about - unless it's shared. Active listening and putting in real effort to trace a thought that lead to an idea is what will actually make you smarter.

Chances are you'll still disagree with some things, and oppose others. That's fine, otherwise communication would serve no final purpose. But rejection can be opportunity, and this is why communication needs to be a two way street, not some boulevard of stop signs. There's an opportunity to explain your side of things, your reasons, motivations – your experience, environmental particularities or implicit assumptions. And even biases, if identified as such. But there's just such a striking difference between saying no – an unhelpful rejection and explaining opposition to an idea. There's learning in explanation, even if it's to understand someone else's opinion better.

Maybe no is not a boring word, maybe it's just super not helpful. No, don't use it.

feedback

We're running regular surveys in which all employees are asked to rate various aspects of their workplace, and that gets then summarised and condensed and gives me, as a line manager, the opportunity to understand what the people reporting to me think of the company, the team and their environment. My general expectation is to load without surprise, that is, lead in such a way that no action or feedback in itself comes as a surprise. So I was surprised when the results of one of those surveys were actually not great.

And then I thought back to a piece of advice I've read or gotten a while back – one of the most valuable pieces of advice from my perspective: Do not react to feedback in the moment. Probably mostly applicable to feedback that is delivered in a more direct form of interaction, it really got me thinking. On safety, on feedback and on how to get to a no-surprise environment as a leader.

Taking a step back, "managing down" with an approach of no surprise is not a piece of cake either. It takes an honest reflection of the delta between expectations and the reality you find in your team - and with your people, and also requires a level of integrity that makes you deliver necessary feedback and criticism in a direct way. To be candid. (By the way, go and read Radical Cantor by Kim Scott, outstanding read). That's a skill set that can mostly be learned, the important part is to diligently apply it.

Realistically, your reports should neither have to develop that skill set nor have to apply it just to save you from a moment of surprise when receiving feedback the other way around. Rather, it's up to you and us as leaders to make it stupidly easy to give feedback, especially on topics that might be perceived as negative by whomever you're reporting to. And receiving feedback, in a leadership role, consists of three distinct phases – and you can mess each one of those up. Time to take notes.

First, you have to create opportunities. Personally, I like to every once in a while ask for feedback in 1:1s, offering plenty of opportunities to share that. One of the lines that I like to use to emphasise transparent handling of feedback is

Hey XX, I was wondering how I'm doing my job from your perspective - and I'd really like to understand if there's something you think I should be doing differently or better. Feel free to share that now or drop me a mail. It's also perfectly fine if you'd rather not.

There's something there that's relevant for me that's probably described as "feedback without cause". I very actively avoid asking for feedback around "loaded" situations, like conflicts, challenges or interventions. Feedback can wait until the ship is in the harbour, no need to ask during the storm. Create opportunities, but be smart about when you create them.

The second thing that works well for me is to create a precedent – show that it's safe to critique you, and that you will not leash out, single out the person criticising or react negatively in any other way. Make it obviously very safe, also in a group setting, to share challenging feedback. In any healthy team, some people will take over the part of helping you create those moments. But you need to be really mindful of the fact that your folks will likely watch your reaction much more than what you're actually responding. Body language, Tone – that's what matters much more than any messaging.

Probably what matters most is to create results – based on feedback. Just taking in feedback without acting on it is the most silly thing you can do. And unless you're a total psychopath it's really hard to not try to improve, either in your own behaviours or by modifying actions you take as a leader. Those results, the actions you take as a consequence of feedback you've received, are the one thing that ties it all together. Asking for feedback is meaningless if it's just noted, written down and then forgotten. Make sure people feel that what they are sharing actually makes a difference.

Create opportunity, create precedence, and create results. That's what feedback needs to be shared. Safely.

great teams

If you take a group of people and have them walk one kilometre over a flat surface, and compare that group to another group that walks a kilometre over challenging terrain - those groups will be different in the end. One of those groups will have a story to tell, an achievement to celebrate and some thing to remember.

One of the biggest questions on my mind is how to establish positive and healthy teams. There's some pretty basic measures, like not hiring toxic people, establishing solid guardrails on what behaviours are expected - and they'll get you to a certain point. But that can never be significantly more than just a starting point.

Looking back, the great teams I was part of were great not because of the shape of the team itself, but because of what we achieved. We thrived through achievement. Progress. Direction. Movement. A team without a mission will never be great, simply because there's a lack of moments that will force a group of people to actually push through and get something done that is more than any of the individuals could have achieved. That's when a group of people become a team. And they'll have frictions on the way, they discover differences - and shared beliefs. But they need a something to experience all of that.

I'm not writing that to give anyone some justification for just throwing random pieces of too much work on a group of people, expecting magic to happen - that's not how it works. While occasional excursions from the comfort zone are necessary to enable or even push for growth, this is not what this post is about. You don't create 10x team by just pushing them to success, you help to establish great teams by allowing them to be awesome. And that's a striking difference.

I started running many years ago. And I don't recall my initial pace, but it was bad (so bad that my brain actually forgot it). The first two or three runs, that made me feel not great. But then something interesting happened: I got faster every time. The achievement was in progress, much more than in achieving success based on some arbitrary outside measurement. I became intrinsically motivated to just get a little better each time.

Probably not all teams behave in comparable ways, but I believe that a lot more can - if given the opportunity. Small projects, achievable work and manageable increments is what helps good teams to understand how to work together - and how to win together. Once that is established, they'll usually push for more. Building something great is a motivator that far outpaces some of the more traditional incentive for doing a great job, and it's not that hard to create an environment that fosters just that.

At the end of the day, one thing to keep in mind is this: Great teams don't deliver awesome things because they have to, but because they want to.

flight levels

It feels like I'm entering some sort of phase where the focus is on some of my mental models. One of those is flight levels. Solutions have different flight levels. Conventionally, a flight level is the altitude at which an airplane is flying. The higher up you are changes significantly what you can see. You can see further. But for details on the ground, you need to fly lower. Otherwise, you've got no chance at identifying individual details. They might matter.

When you're starting to look at a problem, you're probably starting somewhere high up, just discovering the space it's in without diving too much down into the details. And that's ok, it's a useful tool to moderate the cognitive load and make sure you build a solid understanding of whatever you're dealing with – from a high up level. And that's probably a great level to form an initial idea about what could be done to solve whatever problem you have in front of you.

But just because something looks like it might be a great fit from up there doesn't mean it is.

The real work is in helping an idea to make it to the ground safely. This is about bridging the gap between "think big" and "the devil's in the details". This is hard. Ideas are lovely since they can be incredibly convincing, powerful and even paradoxical, all at the same time. An idea without a shape, without something that has been confined to match the constraints of the real world is in itself magical. And that is of course by design, everything that bends reality is magical – and ideas don't have to adhere to real things, because they're just that – not real. The real question you'll find the answer for is: does your idea survive the process of being made to fit reality.

What does that process even look like? Ideas can be very naïve. Say, the idea is "we'll just build a service that converts markdown to PDF Files". That's great, especially because it sounds simple and powerful. But at this stage, this service can be anything anyone wants it to be, since it's lacking a specific shape. And without that specific shape, it'll forever be just an idea.

So to develop a shape you need to ask the hard question. What are the details of your idea? Where is it falling apart, where is it just rebuilding something that already exists? And where are you really solving a problem and where are you just trying to do what you think is right. And this is hard work. Hard, because determining all the details takes time and focus to get right. But the hardest part is to, at times, realise that whatever you were dreaming off high up just doesn't look good on the ground.

So whenever you're discussing a problem, an idea or a solution – know which flight level you're on.

And make sure to land before making a decision.

capabilities

There's words I use a lot at work. One of them is capability. What's a capability?

It's something your system can do, kind of like a skill or feature. If you're building a content management system, and that content management system can display blocks of HTML, and not only rich text, that's a capability. It's also a feature. So what's the difference? Probably there's a formally correct definition, but I like to think of capabilities as features that you can reuse. It's kind of like the product version of abstractions. You can use that HTML block for one specific thing, like embedding a YouTube-Video, but you can also use it for plenty of things down the road that you might not even think or know about at the moment.

When you're an engineering lead, you're also always wearing some sort of product hat – even if it's just being the sidekick to your product counterpart, helping to solve product problems in the right way. One of the ways in which I like to live this is by finding ways to not build only features, but to building capabilities. A feature is always a more risky investment if you can't reuse the mechanic for something else.

Imagine a feature where you need to run a query periodically, and as a result send out an email to some customers – something along the lines of an abandoned cart, where you want to remind people that they can still order their items. There's plenty of ways on how to approach this, but one that focuses on capabilities would try to break down this problem into a few small chunks. Those chunks could be running a query, running something periodically and sending emails from within your system. By building those three capabilities (and making them reusable), you're creating something that solves the problem at hand, but also augments your code base by adding things that can be used at a later point, in different contexts and for different problems.

Capabilities are not abstractions. They are an entirely different concept – you might have the capability to store something, it's not necessarily very interesting how or where you store things. You might have a KV Store, but you might not really care what specific technology is behind that. Abstractions are a different, yet also important angle to consider for some problems, but they're not relevant in this discussion here. You ideally only care about being able to do something, not how something is very specifically done.

Good systems are, from some angle, like something built from Lego. Incredibly robust, but composed of reusable and flexible units that can be moved and applied to a number of challenges. Capabilities are one helpful mental model that I regularly apply when looking at a solution to problems, and maybe it even makes sense to you.

optimise for frequency

There must be a special place where all the abstractions that were never needed, but still built, go when a project is abandoned. Today I want to talk about optimising for frequency. What I mean by that is to make the things you do often during development of a thing really easy, while not getting too busy optimising for cases that rarely happen.

When you build a kitchen, you make it easy to cook food in it - and eventually you also want it to be cozy, or roomy, or practical, or whatever your preference is. But you optimise, very immediately, for the most common use cases. That's good.

Whenever I see an ORM, a piece of software that abstracts implementation details of a database technology away from the developer to make it possible to flexibly switch database systems, I'm chuckling a bit. There's plenty of really good use cases on why you want to use a generic ORM - it might really make your life easier, solve some common issues and so on - but how often do you really exchange your database? I've been in plenty of projects, and that was a very rare occurrence. Upgrading from one major version to a newer one, sure. But completely changing? Rather not.

Same reaction I get when I see folks abstracting away S3 into their generic file storage provider and - how likely are you to actually replace AWS as your primary cloud solution provider? And if so, is the dependency on S3 really going to be an issue?

And we're slowly moving to the point that I want to make: Don't spent time on optimising for things that will never happen. Some codebases feel like the software engineering version of a prepper tried to prepare the system for all possible outcomes, conveniently dropping readability and accessibility on the way. Do not be that person.

Here's what you want to optimise for. Statistically, your code will be read by humans - often. So the first thing you want to do is not optimise for readability, but optimise for clarity. A very obfuscated code base is hard to work in, hard to spot problems in and hard to change. Clean code, easy code and understandable code are rocket fuel for productivity.

Secondly, your code will be built and run very often. Optimise for fast builds and a convenient and easy way to debug both locally and get actionable insights from your production environment - some APM should do the trick.

Thirdly, your code will get changed a lot. So optimise for change. Make it easy to change things without breaking the whole thing, have a QA strategy that supports your team in getting changes out in a safe and reliable way.

And once you're good at having readable code that's fast to run and safe to change, you can maybe think about building some useless abstractions that don't solve a single problem you actually have.

But you can of course also just not do that.

they know

Note: I was not sure if I should publish this post, because it very much feels like i'm putting folks in too broad categories - I'm not, it's one of the many mental models I use at work, when leading teams. I'm writing this with many grains of salt, and I'd ask you to read it with just as many.

Back when I was doing consulting I learned how to quickly join and onboard to new teams. When you're joining new gigs with a certain regularity, it makes sense to get good at that.

I want to believe that I'm still good at that. But since I'm no longer an IC, but mostly active in a leading role, I'm trying to leverage what I learned in a different context to help me do what I am doing now. What did I learn?

There's roughly three types of activities you can do in a software engineering team:

  • Work
  • Talk about Work
  • Anything else

Step one in the onboarding game is to identify folks who're clearly in category one - the doers. Luckily, there's few fields where things can be as easily quantified as in IT, so I usually just start to look for artefacts of work. That can be code contributions, documentation, concepts, really anything that would count as "result of real work".

To the folks reading this saying well, but what about the people who almost exclusively elevate other folks. That's a thing in certain teams and certain organisations, but it's relatively easy to pick up on that dynamic early in the team. And still, how good can you be in elevating folks if you're not really getting your hands dirty every once in a while yourself?

You want to speak and interact most with the people who actually know how the sausage is made. No translation layers, no middle men, just get the message straight from those folks who get the job done.

Now, finding the people who mostly just talk about the work is relatively easy - they'll put in a genuine effort to share whatever their messaging is to you without needing a specific prompt. I can't recall a single team where we didn't have at least one belonging to that group, and they're interesting. Talking is of course absolutely crucial for every time, but it needs to be in balance with, well, actual work. There's roles were the work is talking and discussing and aligning and solving conflicts and making decisions in groups and whatnot, but most regular engineering folks shouldn't have to sit in meetings all day to talk about what they are doing. To me, that's mostly a dysfunction, and one that can be harmless, or one that is an actual problem. Very much depends on the team and wider organisation.

The third kind is weird, but they definitely exist. Folks with little to no constructive output that are kind of just present in teams without really contributing. The problem is that bored folks can wreak havoc in any place, so those need to be watched.

Once those buckets are, somewhat, established (and reality is, of course, far more nuanced), I put in a ton of effort to actually listen to the folks getting most of the work done. Directly, in 1:1 conversations. And one thing I learned there is that surprisingly, they know. They know if the architecture is shit, they know if they're building the wrong thing, they know what a good refactoring would be - it's just that, too often, no one bothers to ask. And no one bothers to listen. You can save the money you'd spent on cloud consultants and simply hang out more with your engineers. They'll tell you pretty much what you need to hear – but you need to actively listen to that.

Do not listen too much to the people who've got nothing to do or those that anyway never stop talking. Listen to the builders. They know.

fast things

When I was 13 or 14 and went to school, we did have a computer room. Probably multiple computer rooms, and they had computers that were running Word and other standard software of that era. Thinking back, one thing I can not not chuckle about is how Word still takes exactly as long to start on my state of the art MacBook in 2024 as it did on, well, the state of the art hardware of 1999. Realistically, I've probably just got a shitty recollection about how slow it really was, I'm discounting the fact that Word also has a million new features and that Mac isn't exactly the platform that Microsoft is investing heavy in optimising for - still, is it just me or is software just not getting faster?

Getting to the point now. It's 2024, and I feel we're not always as honest as we should be with ourselves that most of the stuff we're doing can actually be instant. Pushing data around? Unless it's an absurd amount, that needs to be instant. Redrawing content on a website? Instant. Navigating from one screen to the other and loading some data? Well, almost instant.

And now you look at a bunch of modern apps and we, as an industry have seemingly taken an absurd turn. Instead of having a stern conversation with ourselves that what we're building needs to be faster, we instead have innovated and came up with visually appealing loading indicators and skeletons (Those are the outlines of components that are in being loaded, and will be replaced with real content once that content is there). Loading states aren't something you should spent a lot of your time on. You need to spend your time on making sure you don't need loading screens. But we've gotten lazy, haven't we.

When I'm looking at a profiler and see some call taking 2 seconds, my first thought is: how the hell do you keep a machine busy for 2 seconds to get some product data? Or to update one record? And every so often, there really isn't a reason, it's just that no one cared enough to look into it, remove some silly code paths, add an index or perform any other sort of optimization that's still solidly in the "low hanging fruit" territory. And what we end up with is a bunch of endpoints that are taking a little too long, culminating in something that takes way too long to load, eventually leading to someone having to implement visually pleasing loading indicators and, of course, skeletons.

My advice: Treat slow things as something that must be optimized. A fast system is composed of a lot of tiny, small things. A slow system is just the opposite. And much like the broken window phenomenon, it's not gonna get better unless you put in some effort to actually make it better. Whether it's your data modelling, your overall data flow or any other reason that affect your performance: fix the problem, don't push the problem on your users.

Remember: It's 2024, things should be fast by now.

superpowers

Recently I helped doing some renovations in my daughter's kindergarten. It was a fun weekend project with some fellow moms and dads, and one of the other parents really stood out. While we were all somewhat equally motivated, he brought a very interesting skill to the table: improvising solutions that made problems go away. It was an absolute joy to watch someone look at some situation and just wait for a spark or an idea - and then seeing an idea being put to action.

Well, it got me thinking that this is actually both a transferrable skill set and the one true superpower great engineers have. Making problems go away. Mind you, I'm not saying building awesome solutions - that often comes as part of the "making problems go away". The crucial part here is that the most impactful people on any team are those that can reduce the number of things to work on, and to worry about. Make problems go away.

Naturally, that sounds crazy simple. And indeed there's some traits I've seen in the few engineers that I've seen excel in making problems go away. Let's talk through them.

The first universal trait is some form of curiosity. You will not gather enough critical context, be it business or technological, if you're not naturally curious about the organisation, environment and surrounding you're finding yourself in. If you only look at the ticket straight in front of you, chances are that this is exactly what you'll be working on. Highly impactful people I've worked with had a habit of listening in on important conversations, reading through channels that they're not directly involved with and generally, going the extra mile on gathering context. Having more knowledge is super helpful in problem solving.

The next trait is being connected. And I don't mean networking for the sake of it, but creating trusting and positive relationships to their peers. It's those relationships that are critical for both learning more and enabling fast problem solving. It's not what you do, but with whom you're working on something that sometimes decides on whether an approach works or not.

Moving on, one of the more critical tactics is to focus on getting the right work done, and ignoring or discarding the right process at times. I witnessed outstanding folks disabling every merge check in the book to get something to production in a few minutes. That fix was understood by one person, and at the end of the day saved a ton of money - especially because it was deployed fast. While that situation should've never happened in the first place, it was the right call to not involve half the company to get consensus on a path forward, but to prioritise doing the right and necessary thing. You all heard the famous saying that it's easier to apologise than to ask for permission. That's the right spirit.

The last, and probably most defining behaviour that I've witnessed in those having the superpower is that everything that needs to be done will get done, regardless of stack, functional area or skillset. What I mean by that is that exceptional people that are mostly working on backend topics will find a way to do a small frontend change if that unblocks the team. Or frontend folks doing small backend adjustments. Feeling constraint and locked-in to a certain realm is the most limiting mindset you can adopt, and one that will reduce your impact for no good reason. We live in a time where it's easy to figure out how to do most things in most common languages and environments - leverage that to make problems go away. It's important to get the work done, it's not important what job title the person who got the work done has.

Be curious, be connected, don't be afraid and do what needs to be done.

fast and short paths

Today was interesting. But let's digress first.

When I was first using turn by turn navigation systems, one of the really surprising discoveries was that the shortest and the fastest route are not necessarily the same thing. There are quite some instances where they are actually completely different, like when you go for a longer stretch on a highway, as opposed to a short route on a backcountry road. Very logical once you think about it, but you have to think about it. What's interesting is that the correct answer very much only depends on the question you're asking. If you want to go fast, you don't want the shortest, and if you want to travel as little distance as possible, you don't care which one is the fastest.

Today was interesting. I was involved in a discussion that basically asked the question - should we go fast or should we go clean. (I thought about using short here, but realistically, what short here means is the clean alternative to going fast). Now, I'm the first one in the universe to be content with a fast solution, but there was something interesting about the particular problem at hand, and especially about the fast solution proposed. It gave me food for thought, and obviously here I am writing about it.

Truth be told, it's one of my favourite activities to be called in to help make a decision on some technical matters. It's like eating the inner part of a pain chocolate, the reward for a lot of other parts of the job that might not be as satisfying, but more necessary. In those discussions I usually try to get to the gist of the problem and then moving on to trying to understand the solutions. Being repetitive here is also a useful tool to ensure there's a common understanding of both the problem and all available solutions in the room.

Today the problem was rather clear, the interesting part were the nature of the solutions. There was a very fast solution, and a short (but slow) solution. The fast solution sounded more appealing to begin with, but understanding the problem more and more, there was a substantial problem with it: it was as unintuitive as it was fast.

What I mean by that is that, even though the proposed solution was certain to solve the problem at hand, it did so in a way that's incredibly unreasonable without the specific context of the situation and discussion at hand. You wouldn't understand why stuff was built in that way a month or even a year from now. It would just be massively confusing.

It was rather easy to decide against that one, in favour of the shortest path – that might take a little longer to travel.

The gist? There's short paths, there's fast paths. All of them are fine, but don't sacrifice simplicity for the sake of speed. A fast solution now might make you slow a year from now. And you wouldn't want to upset future you, would you.