Skip to main content

Culture as Code: The Team Playbook for Quality Software

··4971 words·24 mins
Culture Working in Teams Leadership

Building Software is a team sport. Act like it.


Why culture matters
#

usa 2004 olympic basketball team

The 2004 USA Men’s basketball team had Allen Iverson, Tim Duncan, Carmelo Anthony, Dwyane Wade and Lebron James. Still, they lost by 19 to Puerto Rico and were also defeated by Argentina and Lithuania ending up with a disappointing bronze. Team USA had 2 losses altogether in Olympic’s history so far.

houston rockets end of 90s

Towards the end of the millennium, in the late 90s, the Houston Rockets had Hakeem Olajuwon, Clyde Drexler, Charles Barkley and Scottie Pippen on their roster, hall of famers across the board. Yet, no title and a lot of drama.

real madrid galacticos

The early 2000s Galacticos squad of Real Madrid was loaded with star power: Zinedine Zidane, Luis Figo, Ronaldo, Raul, Roberto Carlos, David Beckham… ridiculous. Still, that team failed to win any title (aside from a fucking Super Cup… geez) in a span of 3 consecutive seasons. Just a complete and utter failure.

The point, no matter what individual skill set we got at hand that may totally slay on paper, be it our out-of-this-world test coverage, our super optimized AI copilot setup or our beautifully orchestrated all-world CI pipeline, that’s still no warrant for success. All of those things mean nothing if our engineering team’s culture is a haphazard mess and ain’t fit to successfully compete on the tough court of software engineering. Cause then, the bugs still ship.

Now here’s what’s up: all of that fancy tooling mean nothing if the team using it has no deep-rooted agreement on what their common goals are (what “done” mean anyway) and how to get there together (“what’s that guy doing all the time?”). That’s all culture.

la lakers 2003

And here’s the catch: if you don’t actively shape your team culture with intent, it will just fall into place completely rogue. And the odds that it just so develops in your favor ain’t good my friend.


The myth of more
#

Who hasn’t done it at least once? In case of doubt, just add some more RAM. While this is probably the least sophisticated way of trying to deal with your system, it’s just the engineering default move to the max: adding more. Be it more tests, more reviewers, more approval gates, more AI assistance or more dashboards.

The number of available tooling is multiplying like Danny Zuko’s chills and the discourse tied to it is pretty much overblown. Using this language, that much CI, TDD or die, agent swarms, that language with this LLM though. We are “blessed” with an omnipresence of different and capable tooling and yet, the same problems we had 15 years ago still follow us:

  • lingering bugs in production
  • unreachable deadlines
  • people crashing out hard

The problem is not that the tools and new features don’t do what they should, cause for the most part they actually do. What has become more and more clear is that all kinds of tools are only as good as the people and teams putting them to use, effectively amplifying what skillz they got originally both for the good and the bad. Meaning, if the team foundation is in sync on what they are aiming for and how to go at it, the tools they use will accelerate them. If the team sloppy and working in different directions, then tools will just create more of the same ol’ mess with some added complexity on top.

Especially the whole new wave of AI and agentic engineering have made this painfully obvious, as code is currently shipped much faster and with a lot more overconfidence than ever before. Why is the whole convo on speed and lines of code all the time, anyhow? Why not utilize it to augment our actual work and focus hard on finally producing some pretty darn-good software with those additional resources? But yeah, that in itself is a topic for another day.

So yeah, the secret sauce for software quality ain’t the tech stack. Never was, never will. It’s all about the people and how they workin’ together as a team: about its communication, the decision making and accountability. All those things need be in place, the tooling just plays a supporting role and won’t be able to actually carry a team.


Culture as code
#

As engineers, we tend to feel heavy ownership for our code, we design, review and refactor it, we treat it like our baby, even arguing with each other when giving it silly lil’ names. In short, we have a habit of identifying mightily with it, even to a fault. But let’s be real, while code is a very obvious yield of our work, it’s by far not the most valuable. I mean, it’s really just code, even some stupid-ass LLM can write it (there, I said it!). Thing is, what has real value in it is the people dynamics in your team and how they were built, leading us to the team culture.

giving your code silly little names

What exactly is culture though? Culture is one of those terms everybody kinda uses and has associations with, but when asked to explain, just starts fumbling for words. Similar to Ferdinand de Saussure’s theory on language, we can look at culture as the often completely unspoken set of agreements, rituals and understandings that are shared among team members. Also same as language, culture cannot be directly looked at or influenced. It can only be observed and updated indirectly via it actually being put into practice. It lives in everything done within the team context: meetings, communication, code reviews and so on. All of that constantly shows and shapes the culture. It’s the actual, lived contract between the people on your team about how you work together even or especially when things get messy.

But different than language that runs through whole nations, civilizations and society, we actually got a crack at shaping that thing proactively in our little microorganism: the team.

So, what if we did the same thing we do with our code with the agreements between us?

  • Design your culture with intent. Think about what you actually want it to look like, instead of just letting it fall into place.
  • Review it regularly. Same as code reviews catch code smells, reviewing your team’s culture will blow the cover on cultural stinkers.
  • Refactor it when it’s off. No need to reinvent it every couple weeks, just fix the biggest pain points one by one over several iterations.

Our codebases are shaped by countless tiny decisions made over the course of a project. Same with our team culture. What sets them apart is that hardly anyone treats the second one as something that can be changed and iterated on, but rather something that just happens. Thing is, it can and should be consciously steered and used strategically.


Building software is a team sport
#

What you need to just kill it as a team is trust and care. A team that cares for real about the results they puttin’ out there and each other will get done whatever they set out to do. That is quality in practice: when people care, they don’t let things slide. When they trust each other, they talk openly. Real talk from the get-go will short-circuit failures and foster individual and collective growth. It don’t get much better than this!

That is, cause engineering teams that manage to deliver great software time and time again don’t work like individual contributors, but they have the same setup as a sports team: a common goal, shared values, a game plan, knowing each other’s assignment and mutual trust in everyone else on the team to execute their part. That’s the playbook of rituals and moves that set good teams apart from the rest.

uncultured swine

Having new people join our team is always a litmus test but also a chance when looking at the current state of our team culture. First, if you got something good going in terms of team culture, onboarding will be a breeze. Also you can get the perspective and observations of someone coming in with a fresh set of non-biased eyes. They notice whether our code reviews align with the values on our wall. Is all the stuff we have written down and set out to do what we are putting into practice for real or are we just tellin’ ourselves that? Second, you get a fresh perspective on things that you can use to refine and refactor your culture further using newly added skills and insights from the new colleagues.


The Playbook
#

Putting team culture in practice can be boiled down to a handful of things, none of them super fancy or just in:

  • Shared values: what you stand for as a team, lived in real decisions, not just printed on the wall.
  • Rituals: the small repeating actions that quietly hold the line. Pre-mortems, postmortems, retros, standups.
  • Communication patterns: the interface between teammates. The shape of how information moves.
  • Requirements engineering: the thing that pulls it all together and helps us agree on what we’re actually building before even our first practice/coding session.

Rotten culture is an alignment issue.

Here come the seven plays. Pick the ones that hurt the least to try, but don’t forget: no pain, no gain!

Pick your playin’ style: values that actually do (the) work
#

Every great sports team has their very own style of play which is also part of their identity. The Detroit Pistons teams of the early 90s were pegged The Bad Boys because of their physical and borderline legal defensive style of play. Then, the St. Louis Rams around the turn of the millennium just outscored everyone with their love for the big play, earning them the moniker of The Greatest Show on Turf. And, the Spanish Men’s national soccer team will always be remembered for their tiki-taka style during their World Cup and Euro runs.

Point is, a team’s style of play is a direct reflection of their values, but the team needs to be deeply in sync on those values to be able to translate them into their work. The difference is whether you got some half-ass values written down somewhere and having actual operative values that are put into practice for real. By the way, these are also the kind of values that make up the basis of what gets flagged during code reviews and how that feedback will be taken. Because, if your team’s values are brought up as a rationale and only met with confused looks or angry discussions, whatever you got hanging on your wall is but some cute lil’ decoration.

No matter what you say your values are, your actual values will soon enough reveal themselves in practice, cause it’s not something that can just be memorized but needs be truly internalized. Best way of probing for your values is checking whether they could just be deducted from code review comments. I recommend picking around 3 such as duplication over dependencies, don’t optimize prematurely & blast radius. Then, make it a rule to have every PR comment’s justification be somehow linked to one of your chosen values. After a week, this will be a no-brainer for everyone and the “I personally would do it differently”, “can we not call this Info instead of Data?” & “I don’t like it this way” will be a thing of the past. Look forward to faster reviews and transparent reasoning behind decisions.

🏀 Give it a shot

Pick one of the arguments you keep having within your team with no end. Name the value buried inside it and collectively decide your stance on it. Use that value, by name, during your next code review.

The name of the playcall: settin’ the record straight on “done”
#

Back to language. “Done” is one of those words that gets slapped around every project in some way. But it figures, as it’s one of the biggest asks when building software professionally. Are we through with this or when will we be? In that context, do any of

  • done
  • done done
  • functionally done
  • done for the sunshine case
  • done without edge cases
  • done level 5
  • merged done
  • deployed done
  • done nut not yet tested

ring a bell?

🐯: “How we lookin’ on that feature?”

🐷: “Yeah, I’m done”

🐯: “So, I can go test it?”

🐷: “I mean, the PR’s open.”

Cool, so not done. We’ve all had that exact exchange and we’ve all walked away from it slightly more confused and a hell of a lot more frustrated than before. And it gets worse the tighter the timeline gets, because what do we do when the heat is on? More status checks. More “where are we at?” pings. More everyone reporting some flavor of done that nobody can decode.

This ain’t a new problem and this is actually a thing that is well defined within Agile, because what we are talking about here is nothing else than the infamous DoD (Definition of Done). But as with all things in life, its value is make or break depending on whether it’s actually lived or just written down in a Confluence page, destined to rot away slowly.

ji-he-man

So, sit down with your team and actually agree on what “done” means for you. There’s not really a right or wrong answer for it, it just needs to carry the same universal and non-negotiable meaning for everyone on the team. Then, never let the team forget about it: frame it and put it on the wall, create a chant and sing it as a daily reminder, get matching tattoos. Whatever. Doesn’t matter how, just make sure it sticks.

And here’s the thing: “done” is just the loudest example. Meaning of a lot of things is very subject to who says or hears ’em. The same trap is waiting under every common language that is within the scope of your project. It’s usually the most obvious ones such as “going-live” or something “works”, for example. Still, the fix is the same fix every time: name the shared language, agree on its meaning loud and proud, and check if your understanding matches your colleagues’ from time to time until a whole category of misunderstandings just quietly disappears.

🏀 Give it a shot

If you don’t have a surefire definition of done within your team just yet, create one. After the fact, ask your colleagues to jot down their own versions without looking at yours. Then, compare your definitions and facepalm.

The huddle: rituals before disaster
#

Before a game begins and also before other key moments during a game, you can see sports teams come together in the huddle to touch base one more time, rally around each other and finalize the execution of the upcoming play for the last time before it counts. But, it’s more than a quick alignment, that’s a legit ritual right there: a small repeating action you do before certain situations every single time.

jameis winston huddle eating a W

Engineering teams need their own version of the huddle, and the pre-mortem is exactly that. Twenty minutes at the start of a project, sprint or feature. One question on the table:

Picture right now that it’s six months/two weeks from now and the whole thing went to shit. Why did it? What happened?

Sounds simple, almost silly. It’s not! The magic of a *pre-mortem *is that it gives people permission to voice the doubts they’re already quietly having but wouldn’t bring up otherwise. Nobody wants to be the buzzkill at the kickoff (yet again, cause they’ve just had another one-on-one with their boss about being too negative). Framing it as a completely hypothetical “let’s imagine we already failed” flips that switch. Now it’s not pessimism, it’s perspective.

If you can’t warm up to the idea of the pre-mortem, just have another ritual to touch base and really align with your team members, be it complaint coffee after lunch, or a lessons learned session at the end of the week.

🏀 Give it a shot

Do a 20-minute pre-mortem before your team’s next project, sprint or feature kickoff. Don’t skip it because “we don’t have time”, but treat it with the priority of an actual ritual.

Watching the tape: when things go wrong
#

Mistakes and happy lil’ accidents happen, both to sports and engineering teams. The only reasonable thing to do about it is to look at it close after the fact. For sports teams that means analyzing games by watching the tape, for engineering teams that’s coming together for a postmortem.

Postmortems are also one of the straightest outlets for the current state of your team’s culture. Because, here it will inevitably become clear whether you:

  • have built an environment of trust that lets members of your team speak freely and directly without anxiety.
  • are locked in an atmosphere of fake friendliness and forced positivity that robs the whole drill of its effect and value.
  • or have silently cultivated a toxic setting of aggressive finger-pointing and mutual hope for each other’s fuck-ups out of fear of failing oneself.

A really good postmortem that leaves you with the most value is all about business, not about being nice. You want to talk your system and maybe processes, not act on grudges against other team members or have a go at someone to look better yourself. That comes with two parts that go together like a hand in a glove:

First, whoever is actually responsible for the fuck-up has to readily shoulder the responsibility and own the mistake without getting defensive about it and without trying to make excuses. This is also not necessary, cause if no darts are thrown by the rest of the team, making the whole thing truly blameless. Also, they got to be open about not knowing stuff. Postmortems are about finding gaps to fill. Covering them yet again, won’t do any good.

The other half is completely and utterly refraining from blaming. Neither directly, nor passive aggressively, not by looks. Be professional about it and check your resentment at the door before the whole thing starts. We can use language to our advantage here and just strictly enforce that all names are left unmentioned in the postmortem documents, passive voice is your friend: “the feature passed code review and got 2 approvals”, “the query was tested on staging, but due to the limited data available there, no performance bottlenecks could be observed and it was deemed production-ready”. Everything else is just noise slowing down the process of analyzing root causes anyhow.

mcd passive voice

🏀 Give it a shot

During your next postmortem session, strictly avoid the use of any names, both verbally and in documentation, and see how the vibe shifts.

Coaching each other up: reviews that don’t suck
#

What are coaches for? Having an experienced outside view to critique on how you doing things and bless you with some street knowledge on how to do things better. But the improving is crucial! A coach that just constantly calls out their players and gives ’em shit without working towards getting them to the next level is just that: a shitty coach. Same goes for PRs and code reviews: bad execution should be spotted by the reviewer, then be flagged and the author should get their coaching on how to make it better. So 2 things matter:

  • The subpar execution not making it to gameday (= production).
  • The player (= engineer) not repeating their errors.

Be aware that not everybody is immediately ready to do this and some might never, the same way as not everyone is set out to be a good coach. And, coaches come with different expertise and specializations such as offense or defense-minded coaches. So, your python-guru might not be able to help you all too much with your Spring service.

Code reviews are another one of those exercises where the state of a team’s culture inevitably leaks. Emotional discussions, endless nits about variable names, indifferent approvals cause “whatever” and the need to mediate between the team members every other week: no bueno. That’s just a telltale sign the team has silently thrown in the towel already!

A good review is like a good conversation. This doesn’t mean there need be endless back-and-forth on every thread, but reviewers and authors should be on the same page in terms of context (remember our team values from before?). Refer to your team’s shared values by name (“Blast radius: if this service fails, the whole application will be down”) and categorize by severity, making the fat feedback sandwich as easily to digest as possible. No WTFs for the author, no “whatever” shrugs, no stench from the reviewer’s taste, no need to start explaining from zero every time: simply no ceremonial theater, but actual and efficient value.

🏀 Give it a shot

Flag all comments on your next PR review by the shared value it addresses and severity with appropriate emojis. Only unresolved threads with severity WARN and upward are blocking the merge and it’s all of those that need to have a value referenced. SUGGESTions can freely be skipped by the author.

Gettin’ a game plan ready: requirements engineering, always
#

Professional sports teams don’t go into an upcoming matchup unprepared. The days leading up to each game are full of analyzing every last details about the common strategy, style and preferences of the next opponent. Depending on this, strategy and assignments are laid out. That is the Game Plan: a common goal and the detailed plan on how to get there. We simply need the same as engineers before entering the field (= opening the IDE). And, that we achieve through proper requirements engineering.

It’s not about blowing up the requirement documentation, but quite the opposite. Just being concrete, as short as possible and to the point. Not skipping any questions that pop up during the design phase, but answer all of them, either by getting the necessary input from users, customers and stakeholders or by making thought-through assumptions that check out. Of course, assumptions can be off and things can change, but that’s perfectly fine and cannot be predicted up front anyhow. Being evasive just adds additional lack of clarity from the get-go.

The questions “what”, “why” & “how” need to be sorted out end to end. This is where the bulk of the brain work happens and the output of this exercise is probably even more valuable than the eventual code, cause clear requirements and a matching solution design are the blueprint for the actual implementation. It doesn’t really matter if this process is more collaboration or documentation heavy as long as it works for your team. Thing is, we absolutely want to avoid individual team members work on subdomains as islands drifting away from the trunk of work happening in the project. Reason why we don’t want that is twofold: First, islands tend to lose touch with the bigger picture and secondly, the whole overarching plan only works if everyone is aware of everyone else’s assignment and how that plays into the strategy.

who are you

And this is exactly the spot where communication has to flex the most. Requirements engineering is where technical and operational people have to genuinely understand each other, not just nod through a meeting. When you’re talking to engineers, go deep on the technical details. When you’re talking to product owners, customers or stakeholders, frame things operationally. Answer questions such as “what does this do for them, “what does it cost, “what does it change? And when you’re being deeply technical, always make it a point of linking it back to the operational background. A requirement that only one side of the room understands isn’t agreed on, it’s just written down. Communication ain’t a soft skill here, it’s the API.

Having this process set up as another team ritual makes it feel natural and easily reproducible. Whether this happens during set aside sessions (cough Planning anyone?), domain or knowledge sharing weekly’s, with shared design docs that are peer-reviewed or a combination of it don’t matter. Current state of the game plan (agreed on requirements + proposed solution design) needs to be shared with and known to everyone within the team with a low threshold for everyone to comment on it or discuss potential influence on the bigger picture after new requirements popping up.

You need to have a game plan and your whole team needs to know it by heart to share a common knowledge of “what”, “why” and “how” we are doing things. Alternative is individual contributions becoming worthless or even mutually hurting each other. This actually happens more often than one would expect and that is, cause it ain’t easy to put into practice without a gap. That is, cause you gotta have really good communication among your team members in place for this to work, which means sharing what is relevant without going overboard and really listening to and caring for what the others put out there.

🏀 Give it a shot

From memory, try coming up with what each of your team members is currently working on and how this fits in the bigger picture. Fill the gaps by checking in with them directly.

Celebrating (boring) wins as a team
#

Owning mistakes is one thing, but the other side of the coin is celebrating with your team mates when one of you made a play, meaning they did a good job. Or as the great Bill Belichick put it:

“There is nothing wrong, in fact you should be excited when you make a play. Look at all the work you put into it. All the time you spent. … And your teammates should be excited too. And you can see when we’re playing with energy, when we’re playing with emotion and when not.”

bill belichick smile

Celebration need not mean a weekend full of partying, I’m talking more like a moment of appreciation and a (metaphorical) pat on the back whenever something is done well. Just make the good be seen. Having that be noticed by a colleague of yours goes a long way, trust me.

Truth of the matter is that a lot of the good stuff shaping up is just criminally overlooked or brushed off most of the time. This also an industry thing, cause most of the time the great execution is quite boring and comes with nothing actually happening, nothing going wrong, so you often have to take a step back to see the good as it’s not a slap in the face like the fuck-ups (those will come calling to you anyhow).

code review praise meme

Call out your teammates the next time they do a good job. This can be some slick segment in their code, a good presentation, noticeable progress that they made or just them splitting some load of the project with you. Only thing, it needs to be the real McCoy and not some false praise “just because” kinda thing. This can be done via DM, during the daily, as a fixed end-of-week email or even within code reviews (I swear, you can point out the good stuff there as well). In this case it really need not be a ritual, but the more spontaneous the better. Fixed rituals for that sort of thing have a tendency to feel forced after some time.

🏀 Give it a shot

Try coming up with a list of all the things your team did a good job with the past week and send to your team via email. Everyone will appreciate that and you might be in for a positive surprise how long that list is. (Also don’t shy away from saying nothing if absolutely nothing went our ways this week if that’s the case. Always keep it 💯)


What quietly tanks the season: the Culture Tax
#

Cultural debt piles up the same way that technical debt compounds. Every time you let “we’ll deal with it later” win, be it pointless or disrespectful code reviews, a bit of blame during the postmortem, a team member quietly drifting off towards working as an island or a 20 minute daily where nobody really pays attention to one another, you’re putting a small charge on a credit card. Only difference, on top of every slip, it will additionally update your culture and slowly hollow it out or poison it. That’s the Culture Tax. Quiet. Constant. Compounding. And mostly invisible until you start looking for it.

It’s tough and slow to build a good culture, but culture can go sideways quickly and easily. So, slowly improving with small, deliberate refactors applied wherever there is the most pain is the way to go. Same as with code. There is no full rewrite for culture. You got to patiently shape it one play at a time.


The bottom line
#

You can’t fix everything. You never will and you’re not supposed to. Pick one play. Run it next week and analyze how it went.

  • Spell out your shared values.
  • Define what “done” truly means.
  • Treat your result of requirement engineering as a game plan.
  • Be open about what you don’t know.
  • Celebrate together what you got done the past week.

It ain’t about things immediately paying off, it’s about getting the mental muscles going. You’re sending a message to your team and yourself that culture is something we shape on purpose, which is a shared value in itself and should be communicated that way.

Communication shapes culture and culture shapes the way you communicate.

Great software comes from teams that treat their working together with the same care they treat their code with: design it, review it and refactor it. Go on that journey together as a unit, celebrate together, fail together. If you care about how your culture evolves and you care about each other, you already a winner.