estimations

Whenever we talk about actual work, there's a tendency of trying to understand how long something will take - or any other proxy metric that means the same thing, but in a more indirect way, such as complexity. This can serve a helpful purpose, but I think it's mostly a distraction. Let's talk about estimations!

For context, imagine a random software engineering team, building things. You have a big thing in front of you, like building a new service or system. As you get more acquainted with the requirements, you start drafting an overall design and soon enough, find yourself in a position where you can actually start implementing things. So you create some smaller units that can be shipped sequentially. Like setting up the service, create the first endpoints, you get the idea. But before you jump into action, someone asks the team to estimate the tasks. As in, put some T-Shirt Size, Fibonacci Number or time unit on them.

Now, there's some really valid reasons for wanting to do that - understanding how much effort building something is is in itself a valuable and helpful data point to decide if something should be done. It's also good to understand, roughly, how long something will take - "it's done when it's done" is not good enough in a lot of business contexts, for better or worse. For me personally, I like to estimate things because I need to understand what I'm building before being able to put a fancy number on it. And I think it can drive constructive conversations on how to build something - kind of like the output of a refinement session.

So a few things that provide value - high level, comparative values used to understand the overall complexity. Use this to understand if you want to build something, but it probably doesn't need a detailed concept, just some folks with relevant background doing a high-level guesstimation. More detailed estimations might be useful to help provide business some timelines on when to expect deliverables, that's also something that can be done on a high level with some accuracy, but depending on the context, a closer look might be warranted. And the third case (that I can come up with) is to actually use it to facilitate a discussion in the team - to lead a refinement and to have a constructive discussion on how to build something. If you know how to build something, you're anyway done with the hardest part of building software: figuring out what to do. Estimating is then relatively easy.

One thing estimations can not do, and should not do, is to enable some form of overly detailed capacity planning for a time unit - mostly a sprint. I've seen this time and time again - folks trying to sequence estimated tasks in smart ways to ensure "capacity is filled". This is about as useless as it gets, and here's why. If you're working on something impactful, that whole thing should be front and center for the team - if it's not, why even bother. If something is anyway in focus, it doesn't matter how long the individual tasks take, since the folks working on it would anyway just pick up the next one once they're done with their current one. There's a natural flow state where the next logical task is being picked up. And once all are done, new work is discovered and found, and that new work can then be picked up. The goal is to build a product, not to follow a too narrow plan that has any free minute planned out already.

What I'm trying to say: Use estimations for high level planning and for creating an understanding of the work. Do not use it to steer or control the actual execution of the work.

you need a kitchen

Remember house parties in your twenties? I do, and one of the highlights - and one of the common thing between most parties - was that the kitchen would be the focal point of most gatherings. Since a lot of partying revolved around drinking various kinds of beverages, the kitchen offered itself as an ideal spot, but people would hang out there, even if for no other reason than the fact that everyone else was already there.

There's an analogy here to team communication in a professional environment. In my first job, you didn't need a kitchen. I was sitting with a bunch of folks (4-6 if memory serves me) in the same physical room and we were building software together. You didn't need messaging, or e-mails, or Jira or anything else. You'd literally just talk about the thing, since physical arrangements and team size allowed for that form of collaboration. But that was also 15 years ago, and building software was a bit simpler. But then again the software was. Maybe.

The world has changed crazy much since then, and, accelerated by the occasional pandemic, we've gotten used to working models that are flexibly finding themselves somewhere on the spectrum between completely on-site and completely remote. It's probably rare to find jobs that still expect folks to come in 5 days a week, but I'm sure they exist. It's also increasingly rare to find jobs that allow for full-remote. In my part of the world (Germany/EU), most arrangements these days look for some kind of Hybrid, with one or more days spent in the office, and the rest working from anywhere else. The problem is: Anything on the spectrum that's not clearly remote or clearly on-site is really hard. Unless, of course, you know where your kitchen is.

The really great teams I've been part of had a kitchen. They knew exactly what the right place was to share anything, to think out loud, to comment on ideas, to gossip and to share memes. They knew their kitchen. It all comes down to communication, and communication first of all needs a defined upon place and way in which it happens. There's nothing more chaotic than more than one channel for any given message, and it gets even worse when you multiply that by the number of people in a team or group. And make no mistake, communication isn't always only involving a sender and a recipient, or a sender and multiple recipients - in healthy teams, communication can also just be observed, to gather some context, to find if there's value in inserting oneself in a conversation and so on. That's why the kitchen metaphor is, in my opinion, so striking - transparency is in establishing a central place where communication doesn't only take place, but where it can also be observed, and rewinded. A transparent, persistent and rewindable record of the thoughts of a team.

So, whatever you're working on, make sure everyone knows where to find the kitchen. It's lovely there.

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.