As I had to work more with teams and then supervising teams I changed the abstractions I value from when I was programming solo: Now I care less about my project's function than the structure of the team. There is a saying that any complex project will end up mimicking the communication structure of your organization. I must confess I thought it was silly until I realized it happened to us.
I now favor code that has a lot of independent modules that can have hacky inner code but that must have clear, explicit and preferably simple interfaces. The project's manager role is to design these interfaces and has the last work on their implementation.
This is a bit the Unix way. Every module has to do one thing (and optionally do it well). This allows veteran programmers who know all the subtleties of the programming language to collaborate with rookie programmers who may write okay-ish modules that we may have to rewrite later but that work well enough.
It also allows programmers, these very territorial beasts, to have their own little realms they control and where they are acknowledged. It helps non-tech managers understand who has to be assigned on different issues and evolutions.
> allows programmers, these very territorial beasts, to have their own little realms they control
My 25 years of programming experience says otherwise. The only place where this works is with good(ish) programmers who are assholes and must have their huge, fragile egos stroked or they'll throw a diva fit. I don't hire or work with those people anymore. Neither should you.
Joint code ownership produces better code because everyone wrote some of it and nothing is mysterious.
I agree that my wording maybe gave a wrong idea about the strength of the "ownership". Code has to be readable and commented and peeking into each other code is welcomed, calling for help or reinforcement on a module is recommended. "Realms" is something that is unenforced and that emerges implicitly.
On the other hand, joint code ownership leads to endless discussions about proper whitespace formating, variables naming and accessors. It leads to never-enforced style rules that no one likes nor follows.
> I don't hire or work with those people anymore. Neither should you.
If you are unable to work with some people by refusing to use a code architecture that would allow you to use them, does that really make you a superior project manager?
Don't get me wrong, different projects call for different processes. You don't code a blog framework in PHP the same way you program an embedded medical device. In some cases it is a bad idea to give free reign to individuals.
I simply notice that when it comes to abstracting your code, team structure and general project context is often more important than the project's function. In my case (non critical C# project with easily compartimentalizable functions with diverse team members of different skills, different maturity levels and a propensity to argue over minor formatting details) it meant isolated modules communicating through a well-defined API.
The alternative would have been to fire half the team and make a nicer code in twice the time. Not what I was hired for.
> On the other hand, joint code ownership leads to endless discussions about proper whitespace formating, variables naming and accessors.
That is not my experience. Sure, there will be some bikeshedding in the code reviews, but you get rid of most attack surfaces by agreeing on lint/formatting options and just delegating the task to a tool. For me, proper whitespace formatting was whatever gofmt spat out. Later clang-format with the agreed team-wide options and currently pretty-js. The team can instead focus on more important code issues like the overall structure or bugs in the reviews.
> It leads to never-enforced style rules that no one likes nor follows.
If no one likes, enforces or follows them, you can't really call them rules. If it is the mutual understanding that some style aspects are not relevant, what's the problem keeping it that way? There is no need to let that get in the way of enforcing some aspects of style in order to produce code that anyone in the team can easily work on.
> In my case (non critical C# project with easily compartimentalizable functions with diverse team members of different skills, different maturity levels and a propensity to argue over minor formatting details) it meant isolated modules communicating through a well-defined API.
IMO one of the best ways to train a new programmer is to have them work closely with more experienced people and with the same level of review scrutiny. I don't know about the project you are working on and its timeline, but in the long term I believe this pays off by turning newbies into good, independent programmers that produce readable and idiomatic code. And no one needs to argue about minor formatting details with the tooling available today. It's a self-imposed waste of time if anything.
> On the other hand, joint code ownership leads to endless discussions about proper whitespace formating, variables naming and accessors. It leads to never-enforced style rules that no one likes nor follows.
Seems less a problem with joint ownership and more a problem with poor leadership.
The point was if you’re trying to make joint code ownership work through those things you are already doing it wrong. Want everyone to run with their own style? Fine. You need to make the style sensitive parts of the process work. Whether that’s to adopt local style or something more complex.
If all you can do is not enforce some poor coding standards then you are defacto not a lead.
There's a tension between "everyone writes some of it" and individuals having autonomy to focus and make decisions. If everybody has autonomy in a shared codebase, you end up with a heterogeneous spaghetti of unrelated design decisions and styles overlapping everywhere. To solve this, you normally end up with a hierarchy of authority, where most people have to have their work vetted by seniors. In this process, people lose autonomy and can't fully act on their own vision.
The alternative is to think up new architectures and team structures that allow more people more freedom to work.
If everybody has autonomy in a shared codebase, you end up with a heterogeneous spaghetti of unrelated design decisions and styles overlapping everywhere.
Being free to do things your own way often means choosing not to do something if it's going to negatively impact other people. Once you realise that you need to think about the way your decisions impact other people you quickly realise that compromising on your choices for the benefit of the wider team results in much better code.
> Being free to do things your own way often means choosing not to do something if it's going to negatively impact other people. Once you realise that you need to think about the way your decisions impact other people you quickly realise that compromising on your choices for the benefit of the wider team results in much better code.
This becomes more and more important as the team grows, as there are more feet that you can step on. Working on a large team is really different than a solo or small team. Large teams are as much about communication, mutual respect, and shared goals than writing good code. Unfortunately, communication takes time and energy, and necessarily slows down the entire process, but it's critical.
The Mythical Man Month lays this out nicely. The possible lines of communication in a N person team grows quadratically at N*(N-1), which explains why large teams are often far less efficient on a per-person basis than smaller ones.
Good point. I think what you're hinting at is good if all or the vast majority of people are on board. In my experience this is not generally the case. People usually climb up the authority ladder not because they are emphatic toward others, or because they compromise on their choices to help the team, but because they solve technical problems and deliver product to customers, often while cutting corners and making executive decisions in the code base. More junior and newer people are at the whim of these more senior people, regardless of whatever egregious technical decisions they like to make.
The microservice approach has its own problems but hints toward a direction where teams are small and autonomous, with ground-up freedom on the architecture of their little service and free from draconian oversight. I'm not an advocate of microservices, but it's an approach that hints at something different.
The trouble is that in closely cooperating teams where I worked, people who did what you suggest ended up in submissive position against people who just do their thing ignoring others. If I proactive think about others and you don't, you get to work however you like it oftentimes making my work more difficult - while I am more restricted and have harder time to make my ideas reality.
I meant team without clear responsibilities and "turfs" supposed to work together :). Clear reasonable responsibilities are an awesome thing and kill whole bunch of insecurities and resulting behavior.
in a professional context requiring collaboration, that's an asshole move, and people who do that sort of thing on a regular basis should be reprimanded.
A good alternative to this that I have found is peer review: everybody gets their code reviewed by at least one other person on the team. Everybody gets to work autonomously initially, and everybody gets their code checked by the rest of the team.
Strict hierarchies suck because if you're a leaf in the hierarchy, it's very difficult to collaborate or work with other leafs that are not in your same sub-branch.
A flat fully connected hierarchy is better for small groups, but it doesn't scale well as the number of lines of communications is proportional to the square of the number of people.
A large set of small loosely-connected groups is an alternative. Groups can communicate with other groups on a need basis. This might be a pipe dream. Some would argue that the emergence of an uber-group that every other group must subordinate to is imminent.
That's a non-sequitur, I guess you mean if every sender sends one message per listener. I don't think it's a broad cast if it targets sole recipients per message.
Quite literally, I think, it could be modeled as a vector space in linear algebra. Each signal is in frequency space, which can be found from the fourier transform integrating the temporal space over different bandwidth channels per sender. You get N² only if the message size is proportional to N, if you will.
> If everybody has autonomy in a shared codebase, you end up with a heterogeneous spaghetti of unrelated design decisions and styles overlapping everywhere.
"everyone writes it" in the long term can still allow specific people to write/fix/augment/refactor/etc specific parts in the short to medium term. i think it's a good goal to never have a part of the code that less than two or three people can maintain, but preferably more (depending on size of team(s) and codebase(s), obviously). i think this generally results in better quality code, and higher bus factor is inherently a good thing anyway.
i used to be kind of against style guides and style checking. but a not-too-rigid set of style norms, the willingness to bend or discard them when justified, and a generally well-behaved group of people that can collaborate well and agree that shared understanding has a lot of inherent value... those things together can get you a good codebase where "everyone writes some of it", or at least everyone can deal with most of it, even if people might have areas of expertise. nowhere near a panacea, and not even necessary depending on the team, but it can be useful even if it means people give up style tics they love or tolerate ones they hate. it also mechanically eliminates a lot of the temptation to nitpick a whole class of things that probably doesn't deserve nearly as much nitpicking as it'd get in code review, because everyone thinks their own personal taste matters more than it actually does (me included, that's why i used to not like style guides).
all the above is much easier if everyone's able to keep their ego in check, be collegial, enjoy the challenge of justifying decisions and accepting constructive criticism, etc. i guess that can be an unfortunately difficult setup to come by, but i've been really lucky on that front, by and large.
> To solve this, you normally end up with a hierarchy of authority, where most people have to have their work vetted by seniors.
if you have what i described above, peer level code review with team leads and managers for the occasional tie breaker should work fine the vast majority of the time.
> In this process, people lose autonomy and can't fully act on their own vision.
if you work on a team, you have to work toward shared goals and vision. if a person wants to fully act on their own vision, they should be self-employed in a company of one (though they'll still be subject to market forces). if you're collaborating, you're necessarily involved in a shifting balance of autonomy, delegation, and being delegated to (which is often but not always where the autonomy comes in).
Great points. By "act on their own vision" I mean technical vision, the how to solve a problem, not which problem to solve. In my experience this is where many engineers become depressed and want to quit. They're interested in the problems the company is solving, but when implementing solutions they become bogged down in myriad rules with which they do not agree. Coding style is simple enough to adapt to. Having to follow a technology stack, core architecture, and core APIs that you find unintuitive is harder. In fact I'd venture to say (as a corollary of Conway's law) that companies that follow the "everybody writes some of it" style breed narrow monocultures, where everybody sees things roughly the same way.
> Joint code ownership produces better code because everyone wrote some of it and nothing is mysterious.
I don't think it's one or the other. You need people to be able to jump in and contribute and improve things.
But you also need responsibility for the code as a body of work. If everyone with commit access owns the code, nobody does. As time goes on and it's a snarl of spaghetti code and slapdash design, it turns out it was nobody's job to make sure that didn't happen.
What you said is contradictory. Jumping in and contributing and improving things happens when everyone is encouraged to change the code anytime. When individuals get territorial is when it rots because people are afraid to touch that "other person's code."
That could happen. But if your technical leaders are rejecting patches for ego reasons, you need to correct that regardless of how you organize your code. Worst case, some people aren't cut out for technical leadership.
People need that feel of "ownership", not based on who made something, that's really bad and lead to "not touching Joe's code, no f way", and probably what you said...
But there needs to be a "Joes is responsible for the well functioning of microservices X1, Y2, Z3" - even if there may be 5 other people working on these same and others.
Also, people's "fragile" egos can be turned to your adavantage, as a manager or owner, but that is "black magick" and an oath I have sworn not to share its ways anymore ;) ...
But there needs to be a "Joes is responsible for the well functioning of microservices X1, Y2, Z3" - even if there may be 5 other people working on these same and others.
That implies that Joe has some kind of authority over the 5 other people, I hope. Otherwise that sounds like a "responsibility for blaming only" situation.
There are some people who are easy to joint code with. But there are many people who are not assholes, but joint owning with them is still something uncomfortable to be avoided. Joint code ownership oftentimes means less clear structures and less predictable code, because neither is able to actually execute coherent vision.
Yes collective code ownership generally produces the best overall outcomes. Individual developers or small agile teams need to be able to work through an entire vertical slice of the application to deliver a complete feature that has actual value to users. When you divide up the work by code module rather than by application functionality then the user's needs stop being the primary focus.
But with collective code ownership it's still useful to designate a couple of developers as "stewards" for each major component. They don't own the component, but they take responsibility for training other developers on the internal design, making suggestions on refactoring, and reviewing design proposals and code diffs.
? I'm not a native english speaker and although I get the general feeling, I don't get the exact picture this should paint in my mind... (and I bet it should be a funny one)
The word 'diva' originally described headline singers of opera. The "stars" of the opera world. Nowadays the word is used to describe someone who thinks highly of themselves, and think that their own needs or wants is more important than others. "Throwing a fit" is to start a disproportionally angry argument over something.
So "throwing a diva fit" is to get mad about something solely because it is not how you'd prefer it to be, regardless of what others think.
A “diva” is generally used nowadays to mean someone who complains about everything. So a diva fit would be a diva throwing a fit. Basically the adult version of a tantrum.
Absolutely this. Some people think silos are bad, but it absolutely works brilliantly in terms of team cohesion. No stepping on toes. Few arguments because everyone is responsible for their own stuff. No nitpicking over irrelevant style preferences. Pride in ownership, self accountability, thoughtful decision making (mostly). All this leads to good working relationships that come in handy when cross-realm issues arise.
Every place that had a great team did it this way.
Until someone goes on vacation, their area blows up and everyone else has to panic fix some code that they've never seen before.
As a manager, one of my main jobs is ensuring that the team's bus number is always above 1 and scheduling vacation time so that we always have full coverage should stuff go down. There's a huge difference between a silo and giving someone responsibility for driving the design of a component/module. The latter is great, the former is a failure of the team's leadership. You can still get pride of having built something even if you're building it for the team and, through code review, the team is accepting ownership. But individual ownership of key pieces of code has been responsible for most of the top-10 shit storms I've seen in my career and I'm never letting it happen on a team I manage ever again.
You are right that this is something to consider. In my case the "realm" think was emergent and not enforced at all, but it was a bit implicit that if possible, you ask Bill his advice before rewriting a function of his model. The lead dev was still nosing around everywhere and had authority to call a piece of code defective and impose a rewrite.
One of the important function of the project manager is also to communicate clearly with upper management about what the team is and can do. We were working on a product that was not deployed yet and our priority was to release a functioning version ASAP, so redundancy was irrelevant. We had n features to develop, assigned to various developer. When one developer is on holiday, their features did not advance. We worked around it thanks to our modular architecture.
It meant that sometimes, <urgent but usually irrelevant feature> was impossible to code before <meeting with potential client next week>. I scheduled or denied those. This team and project structure is optimized for fast development with an heterogeneous team, not for reactivity.
There are infinite ways to organize teams so your concerns are addressed. For example, you can have primary, secondary, and backup developers for each component of the system. That keeps you bus factor down and makes it more clear what the progression of responsibility looks like.
But, absolutely, a lack of peer review, authentic feedback, and general teamwork causes all sorts of issues in so many ways.
I understand your concerns. That never became a crisis for us because of a few reasons. 1. An individual rarely deployed their stuff right before vacation (because of this reason). 2. We had an exhaustive QA process (full regression had to pass before deployment). 3. Everyone on the team was senior and could debug other people's code quickly and easily. 4. Other people's code wasn't crap (see #3). 5. Everyone was familiar as to what the other modules were doing.
Just to note, this wasn't a small system. At least a million lines of code and it was a decision support system. Bugs could theoretically cost lives (at least that's what we told ourselves, but it was kind of a stretch).
This was in a software shop back when we cut and distributed CDs. We had a 3 month release cycle which really enforced #2. I understand this is a lot harder when you are doing 2 week release cycles in Agile or something. With 2 week cycles, full regression for every release kinda goes out the window because there isn't time. (one of the down sides to Agile).
I believe so too, but it's not even that much a function of team structure. A proper design will have lots of places that are mostly self-contained at various levels of abstraction - more than people you have in your team. I believe work should be distributed among those boundaries, because it lets people agree on the interfaces and work mostly in isolation.
Also, most of the time, small self-contained units of code are trivial to fix if the original developer goes under the bus, because small means any half-decent developer should be able to read the code[0] and understand it, and self-contained means you're not afraid to change things because you can easily track how the effect of your changes propagates.
--
[0] - an activity that people often shy away from, or are afraid of. Can't for the love of God understand why.
Are you being paid per written line of code or something? I just can't think of a reason to believe as a programmer that your job responsibilities don't include reading and understanding code.
Unless one's in constant crunch mode, just hammering out whatever crap that makes tickets disappear, there's always time for that in programming. Often it's just jumping into a library method and looking around instead of opening a new browser tab and typing "how XYZ works stackoverflow".
> It also allows programmers, these very territorial beasts...
They should be. It's difficult to get quality experience if you're only designing by committee or executing on someone else's designs.
In other words, carving out niches for people is a great way to get your engineers to level up from junior to senior to beyond. They should, hopefully, appreciate the clarity in career progression also. They should be able to see themselves making bigger and more important decisions as they prove themselves.
I refuse to work with people which such fragile egos, but I agree on the architecture.
Having a lot of individual pieces that connect using simple, straightforward interfaces is much more flexible and maintainable in the long run - it's the same reasoning that draws people to microservices.
> There is a saying that any complex project will end up mimicking the communication structure of your organization. I must confess I thought it was silly until I realized it happened to us.
Is this necessarily a bad thing? I know this quote and it always made a lot of sense for me. But I never understood where exactly is the problem, and why one should go to great lengths to avoid this.
Moreover, in this concrete example, isn't assigning modules to individual developers (as opposed to collective code ownership) essentially the same? i.e. structuring the code along the organization communication structure?
I now favor code that has a lot of independent modules that can have hacky inner code but that must have clear, explicit and preferably simple interfaces. The project's manager role is to design these interfaces and has the last work on their implementation.
This is a bit the Unix way. Every module has to do one thing (and optionally do it well). This allows veteran programmers who know all the subtleties of the programming language to collaborate with rookie programmers who may write okay-ish modules that we may have to rewrite later but that work well enough.
It also allows programmers, these very territorial beasts, to have their own little realms they control and where they are acknowledged. It helps non-tech managers understand who has to be assigned on different issues and evolutions.