Skip to main content

Culture as Code: The Team Playbook for Quality Software

··4740 words·23 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 full of 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.

So, no matter what individual achievements you got that may slay on paper, be it your out-of-this-world test coverage, your optimized AI copilot setup or your beautifully orchestrated CI pipeline, the bugs still ship. All of those things mean nothing if your Engineering team’s culture is a haphazard mess and ain’t fit to successfully compete on the tough court of Software Engineering.

la lakers 2003

Now here’s what’s up: your fancy tooling mean nothing if your team has no underlying agreement on what your goals are (what “done” mean anyway) and how you get there (“what’s that guy doing all the time?”). That’s all culture. The thing that is constantly put into practices, but cannot be easily seen from the outside looking in. It’s somehow similar to language as it is only ever observable in its use (similar to utterances of language) and only then allows to connect the dots on what is the underlying concepts. The actual, lived contract between the people on your team about how you work together even 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. And here’s the catch: if you don’t design that contract on purpose, it just falls into place unexpectedly, not steered. And the odds that it just so develops in your favor are not 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 software quality tanking, 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, strictly stick to test-driven development with AI agents. We are “blessed” with an omnipresence of different and capable tooling and yet the same problems we had 15 years ago still show up: lingering bugs in production, unreachable deadlines and people crashing out.

The problem is not that the tools and added features don’t do what they should, cause for the greatest part they do. What has become more and more clear is that all kinds of tools are still tied to the people and teams putting them to use. This makes them effectively amplify their original skillz both for the good and for the bad. Meaning, if the team foundation is in sync on what they are aiming for and what “good” means, the tools they use will accelerate good. If the team is a sloppy bunch, then tools will just speed up that same sloppiness.

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 confidence than ever before. Also, why is the whole convo on speed and lines of code all the time? Why not utilize it to cover our backs 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. Instead, it’s all about the team: about its communication, the decision making and accountability. When that is 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 about names. In short, we have a habit of identifying mightily with it, often even to an unhealthy degree. But let’s be real, code is just code, even some stupid-ass LLM can write it (there, I said it). Thing is, what really matters is the people dynamics in your team, leading us to the culture

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 see team 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.

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 about the result and each other for real 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. What else you want?

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 to execute their part. This is the playbook of rituals and moves that set good teams apart from the rest.

Having new people join your team is always a litmus test and chance when looking at 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.

If a new hire can describe a team’s playbook by the end of week two, we got a culture. If they can only describe the tech stack, we don’t.


The playbook
#

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

  • 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.

Plus one thing that pulls it all together: requirements engineering — the upfront work of agreeing what you’re actually building, before the typing starts.

Seven plays. Pick the ones that hurt the least to try.

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

Every sports team has their very own style of play which is part of their identity. The Detroit Pistons teams of the early 90s were pegged “The Bad Boy Pistons” 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 teams will always be remembered for their “tiki-taka” style during their World and European Championship 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 even translate them into their work. That is the difference between having some half-ass values written down some where and having operative values that are actually put into practice. These are the type of values that make up the basis of what gets flagged during Code Reviews and how that feedback will be taken, just to name one legit example. 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 reveal themselves in practice, cause it’s not something that can simply be memorized but needs to be internalized. Best way of probing for your values is checking whether they could just be deducted from code review comments. Pick 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 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 the single most repeated argument your team had in the last month. Name the value buried inside it. Use that value, by name, in your next review comment.

The Name of the playcall or Setting the record straight on “done”
#

Back to language. “Done” is one of those words that gets slapped around every project in some way, as it’s one of the biggest questions 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

ring a bell? “Is that feature done?” “Yeah, done done.” “Wait, like merged-done or deployed-done?” “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 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.

So sit down with your team and actually agree on what “done” means for you. There’s not really a right or wrong there, it just needs to carry the same universal and non-negotiable meaning for everyone on the team. Doesn’t matter how your team handles it: framing it and putting it on the wall, creating a chant as a daily reminder, matching tattoos. Whatever. Doesn’t matter how, just make sure it sticks.

And here’s the thing: “done” is just the loudest example. 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. The fix is the same fix every time: name the shared language, agree on it out loud, 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 4-to-6-bullet definition of done just yet, create one. After the fact, ask your team colleagues to jot down their own versions without looking at yours. Then, compare and discuss.

The huddle (a ritual: the pre-mortem)
#

Before a game begins and also before 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 playcall one last time before it counts. But, it’s more than a quick alignment, that’s a ritual right there — a small repeating action you do every 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: imagine it’s six months from now and this thing failed catastrophically. 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, because nobody wants to be the buzzkill at the kickoff. Framing it as “let’s imagine we already failed” flips that switch. Now it’s not pessimism, it’s the assignment.

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 lessons learned 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 after 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 state of your team’s culture. Because, here it will inevitably become clear whether you:

  • 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: 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 no darts are thrown by the rest of the team, making the whole thing truly blameless.

The other half of blameless is being openly OK with not knowing. “I don’t know why that query was slow on prod when staging looked fine.” “I have no idea why that flag was set to true, I assumed someone else owned it.” Treat those moments the same way an API returns a 404 — it’s not a failure of the API, it’s just clean signal that the thing being asked for isn’t there. We’re trained from school onwards to never admit we’re lost, but in a postmortem that instinct is exactly what turns a useful analysis into a defensive performance. The team learns nothing from confident guesses. The team learns a lot from honest “I don’t knows” that point at the gaps in how the system is actually understood.

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.

Give it a shot

During your next postmortem, strictly avoid the use of any names and see how the vibe shifts.

Reviews that don’t suck, cause they are actually coachings
#

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 a shitty coach. Same goes for PRs and code reviews: bad execution should be spotted by the reviewer, 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.

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 can immediately be spotted. 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 and 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 - 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 severity ‘WARN’ upward are blocking the merge and all of those need to have a value referenced. ‘SUGGEST’ions can freely be skipped by the author.

Setting up a game plan (requirements engineering)
#

A common goal and a plan on how to get there

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 that checks out 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. 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.

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, customers or stakeholders, frame things operationally — 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 an interface.

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

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.

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 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.”

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).

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.

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 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, absolutely nothing went our ways this week if that’s the case. Always keep it 💯)


The Culture Tax or What quietly tanks the season
#

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 or poison it. That’s the Culture Tax. Quiet. Constant. Compounding. And invisible until you look directly at it.

It’s tough and slow to build a good culture, but it 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 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 training the necessary muscle. 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.

Rotten culture is an alignment issue.