Conway's Law
If your organization has coders continually getting in each other's way, or if you make a change to part of your code base and don’t know who needs to review it, or if you have code that nobody maintains because everybody owns it... you may be violating Conway’s Law.
Hi -- I thought I'd do something a little different this time. We’ll be back to more Better Know Ruby things next time, but I’ve been sitting on this essay for a while and wanted it to get out.
You can also find this newsletter on the web on Buttondown’s site at https://buttondown.email/noelrap/archive/conways-law/, where you can also comment on it.
If you like this and want to see more of it in your mailbox, you can sign up at http://buttondown.com/noelrap. If you really like this and would like to support it financially, thanks, and you can sign up for a Monthly Subscription for $3/month or an Annual Subscription for $30/year. Subscription money goes toward paying for Buttondown.
I’m planning to send out an audio version of this post as the first subscriber only thing, mostly to see if anybody finds it useful.
Also, my current book is Programming Ruby 3.3, you can purchase it in ebook from Pragmatic or from Amazon.
Thanks!
Conway’s Law is the idea that the structure of a software team and the structure of its software tend to mirror each other.
Sometimes people hear about Conway’s law and file it under jokey rules like Murphy’s Law. I'm going to try to convince you to take it seriously -- there are real benefits that flow from observing it, and you can actively try to make those benefits work for you. And if you don’t, the costs of not following it will for sure work against you. All kinds of dysfunction in software teams are partially caused by consequences of Conway’s Law.
What actually is Conway’s Law:
According to Wikipedia, the original formulation from Melvin Conway was:
[O]rganizations which design systems... are constrained to produce designs which are copies of the communication structures of these organizations.
The book Structured Design by Ed Yourdon and Larry Constantine has a slightly pithier version:
The structure of any system designed by an organization is isomorphic to the structure of the organization.
Neither one of these quite gets at what I use the term for.
Here’s my version, I’m still workshopping it:
Software teams perform best when the relationships in the code mirror the relationships of the team.
In order to convince you that this is a real thing in the real world, and not a joke like Cole’s Law1, we need to talk about software teams.
Okay, What About Software Teams
From a high enough vantage point, a software team does two things: “software”, and “process”. For our purposes we can define “process” as “anything a team does to communicate information about the software”. That’s meetings, it’s documentation, JIRA tickets... and so on.
However you define “process”, it has two important features:
- Teams need to do it, or the project quickly becomes a mess.
- The amount of process a team needs to do grows significantly faster than the size of the team.
You likely have an intuitive sense that a five-person team doesn’t need to do much extra work to keep everyone on the same page, but a 50 person team does, and the only thing I need you to believe for the rest of this to make sense is that the 10x growth in personnel between 5 and 50 leads to much more than a 10x growth in process.
That’s not because people are engaged in busy work, it’s because it’s genuinely hard to keep 50 people all working toward the same goal. And even a 50 person team is pretty small, the problems only get more acute as the teams get bigger.
Which brings us back to Conway’s Law: Conway’s law is a rational consequence of trying to minimize the amount of process you need to keep your team running.
Growing a Team
Let’s take a new team. There are, say, three people at the beginning of a project. As you try and make decisions, there are only three different one-on-one conversations that are even possible. Communication is simple. Everybody fits around a small table, it’s easy to understand everybody’s constraints.
The team has grows and suddenly you have like 12, 13, 14 people. You don’t all fit in a room together for meetings, and coordinating decisions becomes increasingly complex. People are getting cranky, the manager is overworked, and you can’t write a line of code without tripping over somebody else’s line of code. (See https://noelrappin.com/blog/2020/12/the-road-to-legacy/ for a discussion of how this applies purely to code...) The team size has quadrupled, the amount of process has gone up much more than that.
A clear solution presents itself: splitting your 12 person team into two six person teams. This potentially lowers your overall communication complexity. I don’t want to oversell the math here, but a team of twelve has more than 4x communication paths than a team of 6, so the amount of process needed to support two teams of 6 can be significantly less than one team of twelve, even taking into account that there’s now an extra layer of coordination between the teams.
Even if you don’t believe the math, you’ll see it in your teams. Each team fits now in a room (or, the 2024 version, you can have a team meeting and peoples’ faces are large enough on Zoom that you can see facial expressions...). It’s easier to coordinate and plan each individual team’s work.
But... you only get the benefit if the communication between teams is limited. If every person on team A still has to regularly coordinate with every person on team B, you haven’t saved anything. In order minimize process cost, teams need to be able to work mostly independently.
So then the question becomes, how can we limit the amount of communication between the teams?
You do this by giving each team a specific responsibility within the software. You can then can reenforce this responsibility by giving each team its own Rails Engine, or microservice, or Packwerk package, or subdirectory of app/worker
or what have you. If the responsibilities are encapsulated then teams don’t need to coordinate about their internal work, they only need to coordinate about the interaction between the two areas.
There are two teams, and two clear sections of the code. Repeat as needed, and eventually you will have many teams, and many defined sections of the code.
And... that’s Conway’s Law. The structure of the code mirrors the structure of the team. It’s not a coincidence and it’s not a joke. It’s a real strategy that you can use to reduce the amount of overhead that your team has to manage.
Using The Law
On some level, people understand this almost subliminally. In that mythical 12 person web development team, a really common first split would be front-end vs. back-end, because those are two parts of the code that are relatively easy to manage in isolation, with only data structures going back and forth.
You would be unlikely to suggest that we split into two teams, and one team take the Rails resources with names that start with A-M and the other take N-Z. Which is a split in half, but not one that allows for a clear communication path from one half to the other.
Once upon a time, I worked on that was in a position to grow like that building a ticketing app. Front-end vs. back end was be a logical breakdown, so would user-facing vs. internal-facing. But you wouldn’t do... team a handles everything having to do with users and team b handles everything having to do with tickets. The two resources intertwine too much, and the teams would wind up back in the free-for-all communication pattern.
With Conway’s Law in mind, we can go back to some of the issues I mentioned in the opening paragraph.
Very early in my career, I wound up as team leader for at team that had three people in Chicago, and six people in Poland. This was a case where it wasn’t so much that the team size had gotten too big, but that the time cost of communication between the two parts of the team were very high -- basically anything that was a question from one side to the other cost a day because of time zone lag.
In retrospect, I should have worked harder to give each part of the team a separate area of control, which would have minimized the amount of time lost waiting for the other group to answer a question. (In this case, after the second time that developers in both groups accidentally duplicated each other’s work, I built a common task tracker for both teams to use, which was the first real Rails project I ever did, so it worked out great for me. In this analysis, the task tracker is minimizing the cost of some cross-group communication, partially mitigating the team structure problem. But it would have been better to actually address the structural problem.)
Conway's Law and Resilience
The danger and opportunity of Conway’s law comes as the organization continues to grow and change. Everybody understands the logic behind separating a front-end team from back-end team, but then you add 10-15 more people and you need to split again, and the answer is much less obvious and much less consistent between projects.
The problem is that the organization can change faster than the code does, and if you put a lot of effort into making, say, every team have its own Rails Engine (or Packwerk package, or micro service, or subdirectory or what have you), you paradoxically have both a system that is aligned with Conway’s law at the moment, but one that is not necessarily resilient against organizational changes.
And “organizational changes” doesn’t mean “a new VP came in and scrambled everything”, it could just mean “the team is growing” or “we’ve decided to change our focus”. Anything that upends the carefully constructed boundaries risks causing Conway’s Law violations as the team grows over time.
An anti-pattern I’ve seen at multiple previous jobs is the “everybody owns it, so nobody owns it”. This can happen either in a situation where teams have extracted code leaving behind a common base, or a situation where the common base is extracted first. In either case, as the team grows you have a situation where an often-critical piece of common code has nobody responsible for maintenance, but everybody is potentially working in it.
Conway’s law shows us why this is a problem -- the shared common code requires all the people to communicate over the shared resource and you’re back to exponential process growth.
Some Suggestions
So, what can you do about it? To be fair, if I really knew what to do about it, I’d be a very successful management consultant, and instead I am a moderately successful whatever I am, so take this with grain of salt, but here’s what I’ve got:
Know the signs.
A key skill across technology is knowing when you are in a hole so that you can stop digging.
Signs that you are having Conway’s Law problems include:
- Commonly used tools languish without updates because nobody takes responsibility
- It’s not clear who should be reviewing your PR
- Teams are continually and increasingly having bugs caused by code changes made by other teams that affect the code they are writing
- It’s not clear who needs to approve new functional or design changes
The idea is to keep an eye on problems that are caused by communication deficits or muddled ownership so that you can catch it when they increase.
Make Ownership Clear
This is maybe the lowest hanging fruit, a thing you can probably do in your organization right now that will genuinely help communication inside your team.
One thing I believe about organizations is that, at any scale, they work best when there is exactly one person responsible for making sure any specific piece of work gets done.
And one thing I believe about code organizations is that, at any scale, they work best when there is exactly one team responsible for changes in any specific area of code.
You should make this ownership explicit -- GitHub has CODEOWNERS, or you can roll your own thing, but every team should know what code they cover and every part of the code should have a discoverable team that covers it.
Be Aware of Boundaries And Systems
This is sort of an extension of the previous point, but the spaces between teams and the combination of teams are also parts of your system that need to have a responsible party.
You have a front end and back end team that communicate via GraphQL? Somebody needs to own that schema. Common transport layer between all your services? Somebody needs to own it. Obviously, all interested parties should be consulted, but in the end, if there isn’t one team whose remit specifically includes “this thing stays working” it’s going to degrade.
Similarly, processes that go through multiple teams should have an owner for the integrity of the entire process. You have a checkout flow that has a front end, a back end, analytics, money management, and they are all on separate teams? One of those teams (or a manager covering all of them) needs to own the process as a whole.
Decouple Teams and Code, at least a little
The theory here is that if you put a little bit of space between teams and their code, you can make your organization more resilient against future change.
I think there are smart ways of doing this and... less smart ways. I have heard tell of a company that named teams after colors with the idea being that you could keep a cohesive team together and just point it at different responsibilities over time. If you squint, you can kind of see it, but my understanding (I wasn’t there) was that it just got really hard to figure out who owned anything.
I think -- and this is maybe more of a well-developed hunch here -- is that what you might want to do is carve up responsibilities that are smaller than teams, so that each team has a few of them. Exactly how you do it would depend but you’d want a team to have multiple, separately encapsulated (but probably related) responsibilities. The idea being that as the organization grows and changes, teams can just swap sets of smaller responsibilities but those responsibilities are already encapsulated, so you don’t have to create a new break point in existing code.
There’s a few reasons that might not work -- it’s probably a fair amount of extra overhead early on. But I think the idea of keeping smaller bits encapsulated is good for other software design reasons, and it might have the side effect of making the team more resilient to team change.
-
Thinly sliced cabbage. Sound it out. I saw this joke in a joke book when I was single digit years old and I didn't understand it for years. ↩
Dynamic Ruby is brought to you by Noel Rappin.
Comments and archive at noelrappin.com, or contact me at noelrap@ruby.social on Mastodon or @noelrappin.com on Bluesky.
To support this newsletter, subscribe by following one of these two links: