52

We're a small/medium sized company with a dozen or so software developers, developing our own in-house software for in-house use. Given that there's so few of us and there's just so much work to be done, for most part each developer handles a separate part of the system and doesn't share their work much with other developers. Each has their "domain" so to say.

Occasionally however the domains overlap and we need to collaborate; and also the arrangement means that it's hard to replace people and when something goes wrong we must be there to fix things because nobody else can do it (at least not quickly). So this arrangement is both nice (we each have full creative control) and not nice (we're basically forced to be on call 24/7, although in practice it's a bit more relaxed than that).

Recently we tried a little "workshop" among ourselves to promote some better coding standards, namely, unit tests. (Yup, we're one of the people not doing those yet...) During the 2h meeting we aimed to create a small sample program with unit tests, just to get a feel for doing it.

It was a fun 2 hours, and we did manage to produce a little bit of code in the end, however an interesting issue became painfully obvious: having lived so much in isolation for so long, each of us has basically our own coding style.

Now, I'm not talking about tabs vs spaces, camel case vs snake case or some such other cosmetic difference. I'm talking about principles of arranging code. How to name things. What folders and namespaces to place them in. Do I split this code into 3 classes or just one? 5 tiny files or 1 gigantic one? Abstract it away with interfaces and factories, or call it directly? Getters and setters or naked fields? Etc.

At times, writing the absolutely trivial program nearly devolved into a shouting match, although we were thankfully able to retain our cool in the end and no feelings got hurt.

So this got me wondering - how do you normalize coding style among multiple seasoned developers with each their own strong preferences? The different styles certainly are bothersome when we do need to interact with each other's codes, not to mention confusing for any newcomers. And when some piece of code gets handed from one person's domain to another's, there's always the strong desire to rewrite it to match your own ways.

First of all, are there any rules for how to lay out your code? Any standards? So far I've only seen the cosmetic stuff about spaces and cases. Basically how to format your code once it's written (in your head at least). But are there any guides about how to write your code, how to arrange it and how to name it? Where and how to split it in pieces and how to make the pieces interact?

If there isn't a standard and we need to create our own, how do you go about doing that when everyone has a strong opinion of what is right and what is wrong? Now, mind you, we're all seasoned developers here; we realize that none of our approaches is inherently better or worse than any other - just that each one of them has certain strengths and certain weaknesses. But we also have a strong opinion about which strengths and which weaknesses matter the most. So how do you decide on The Right Way™ and how do you make sure everyone sticks to it without hurting any feelings (too much)?

One way I've heard of is to select a Glorious Leader who then forces his preferred style onto others (via code reviews and meetings and whatever), but... you need a really good Glorious Leader who is indeed head and shoulders above others. What if you don't have one, and we're really all equals here?

5
  • I wonder whether the answer is really "coding standards"? It sounds like what you all really need to do is get to know each other's code. No naming convention or arrangement of code files will really substitute for knowing how the application works - what concepts it employs, and the intricacies of its behaviour. Naming conventions will help a stranger understand an application, about as much as standardising on metric bolts allows the stranger to engineering to understand different types of engines.
    – Steve
    Dec 23, 2019 at 14:43
  • @Steve - No, no, naming conventions (as in camelCase), spaces, etc. are OK. It's the what to name that gives a problem, not how to spell it. And that's also what I mean by "coding standards" here - not the form, the content.
    – Vilx-
    Dec 23, 2019 at 20:01
  • Its NOT developers choice BUT Organisations choice (of code style). Dec 23, 2019 at 20:34
  • 4
    @SrinathGanesh - And who in our little organisation would be competent enough to decide such matters besides us developers? Our management is smart enough not to get involved in such matters. If we need their support, we will ask for it and get it, but otherwise such technical details are our own to sort out.
    – Vilx-
    Dec 23, 2019 at 23:18
  • 4
    For code style, configure a formatter. Or use an opinionated formatter like black so that everyone is (un)happy. For other stuff you would probably need to create an internal "coding guidelines" document, preferably democratically written. Dec 24, 2019 at 1:27

12 Answers 12

89
  1. Have a coding standard. If the shop you're going to work for already has one in use, that's the one you follow. Avoid coding standards that are dozens of pages long; it's not that complicated. Instead, look at code you like on Github, and follow that style. Find the well-established idioms for your particular programming language (camelCase, etc.), and use them.
  2. Let the IDE and code style tools do most of the work for you. For example, Visual Studio already has most of the important rules in place. Don't like how a piece of code is formatted? Ask Visual Studio to reformat it for you. Problem solved.
  3. Hire software developers that know what they are doing. They'll write good code without having to slavishly follow a style guide.
  4. Have code reviews. Seek consensus based on function and readability. Don't like the consensus? One person is the tie-breaker; their decision stands.
  5. Don't strive for perfection; you'll never get it (for many, many reasons that are outside the scope of this question). Instead, strive for improvement.
  6. Try not to waste a lot of time and money on things that don't matter. In particular, don't rewrite code that already works and is already well-tested, just to satisfy your own sensibilities about what that code should look like.
  7. Learn how to write functions and methods that do one thing and do it well. If you do that, your naming should take care of itself (the name is a short verb phrase that describes what the function does).
  8. If you're doing OO, learn how to write small classes that have one responsibility/point of modification. If you do that, your naming should take care of itself (the name is a short noun phrase that describes what the class is).
  9. Learn how to write code that is easily testable. If you do that, the unit tests will take care of themselves.
  10. Pick your battles. Does that small, obscure proclivity that one of the developers exhibits really matter? Avoid religions and dogma.
  11. Finally, remember that the code is not yours. Don't be sentimental about it. You should "own" it and make changes as required (so, in that sense, it is yours), but it's there to serve a purpose. Being overly attached to the code only gets in the way of that by preventing objective analysis of the pros and cons of large-scale changes or decisions about the structure of the code and making you object to compromise.
1
  • It couldn't be better described than with this answer. I've seen so many conflicts about standard enforcement, there are not two engineers with the same opinion, even among experienced devs. The best ones are the ones who can adapt and not lose productivity over so futile things. And who understand the importance of adapting their code to the local standard. Don't write camelCase to a file already written in snake_case, nor the opposite.
    – Tim
    Dec 24, 2019 at 8:35
13

The first step (and it sounds like you're already well on the way to this) is to get agreement from all of the developers that there is a problem, and it needs to be fixed. They all need to understand that they may need to change some of their habits for the greater good. It's a near-certainty that everyone will have to change at least one thing about the way that they write code, but hopefully will also get to continue doing some things in the way they always have. Everyone must subscribe to this.

Secondly, you need to identify exactly what the differences are and write them down in a list. As you correctly point out, some of this is trivial and cosmetic, and some is much more logical. But it needs to be captured in words so that you can tame it.

Thirdly, you need to find the low hanging fruit, perhaps using some sort of voting system among the developers. Some of your differences are going to be difficult to resolve, as all developers have a strong preference and it's an even split. However, some will be more of a 75/25 split, or they'll be evenly split but one side feels much more strongly about it than the other. Those issues are easier to resolve. Identify them, and take them one at a time. Let the developers know the result of the poll, and that all new code going forwards will conform to the group's preferred approach.

Finally, you need a way of enforcing the decision, and the solution to this is simply code review. ALL changes must be reviewed by at least one other developer. This is a good protocol even if you weren't trying to normalise coding style. Reviewers should look for potential bugs, unclear code, misleading comments, and of course, conformance to the coding style guidelines.

Once one change is bedded in, move on to the next. As the developers get used to this process, they will become more open-minded as you approach the more contentious issues.

3
  • I like this a lot! Have you seen this work in practice?
    – Vilx-
    Dec 23, 2019 at 11:13
  • @Vilx I do not have first-hand experience of the same situation that you are going through. However, my suggestion above is based on some basic principles (empowering competent developers, seeking consensus rather than ruling by decree, incremental change, collaboration) which are proven effective management techniques, and it is how I would approach your challenge.
    – Pete
    Dec 23, 2019 at 13:31
  • 4
    Also, The first step (and it sounds like you're already well on the way to this) is to get agreement from all of the developers that there is a problem, and it needs to be fixed., you need to be prepared for people to not agree there is a problem, or that it needs fixed. That's an OK outcome--there may be more important things to work on. Dec 24, 2019 at 1:04
6

Your stated goal for the meeting was to discuss Unit Tests to improve code quality among a group of experienced devs. You then found yourself arguing about:

How to name things. What folders and namespaces to place them in. Do I split this code into 3 classes or just one? 5 tiny files or 1 gigantic one? Abstract it away with interfaces and factories, or call it directly? Getters and setters or naked fields?

None of the above topics are critical to Unit Testing, they are purely coding style, and I would expect any seasoned developer to be able to jump into a codebase with any of the aforementioned options and write stylistically compatible code without hesitation.

Therefor, I would take a step back, realize that you got into a debate over nothing truly important and get back on track with the topic of Testing (not just limited to Unit Tests).

Stress the importance of Tests among the developers and give them time/space to write those tests in a way that best matches their individual style. As long as they clearly document where/how to find/run the tests for their code, then you are fine.

1
  • 4
    Yes, but the unit tests isn't what this is about. What I'm saying is that during our talks we (or, well, at least I) identified a different issue. And that's what this is about.
    – Vilx-
    Dec 23, 2019 at 19:52
5

Dont't have a coding standard other than what is customary for the platform. And assume that your developers are all adults.

Two rules: If you edit a file, you adapt to its coding style. And new code is written in the style of the developer who writes it.

And be tolerant. No need for shouting. Who shouts is wrong.

3
  • 3
    With "style" I didn't mean formatting but rather more philosophical approaches, like getters/setters vs naked fields; immutable objects vs mutable objects; how to group related functionality; etc.
    – Vilx-
    Dec 23, 2019 at 11:14
  • 2
    @Vilx- I suppose the key question is does any of that matter? There are times when one is clearly better than the other (which would argue against any sort of coding standard, as the standard would just say "use your judgement) and times when it doesn't really. And I could easily understand code written in either style Dec 24, 2019 at 13:43
  • That a bad rule which leads to inconsistent code (even if isolated in different files) and frustration (should you comment on someone's style in code review? Would it seem like a nit-picking? Should you let it slide even though you don't agree with it?). Note that this mainly applies to what is usually understood by "code style" (i.e. spaces, indentation, naming, use of features, line length/splitting, etc.), not some general coding practices (how to implement thing X).
    – Dan M.
    Dec 25, 2019 at 11:04
5

I think you are trying to solve the wrong problem. As I understand, your problem is that every developer is working in his silo and other developers only jump in if problems are serious. Of course, having a common (deep coding) style can help in this case but I think the better option is to break the silos first. As long as you are working in isolation, everybody will continue to code in their standards. This does not need to be malicious intent (because you are not agreeing with the standard), it may just be different interpretations. We have our coding standards for years with the same core team for years but still run sometimes into the problem that a code review reveals different interpretations of the coding standard for different people. Which is by the way not a bad thing as only discussion and disagreement allow a coding standard to evolve.

So my recommendation is to try and break the silos first. Maybe there is a new project upcoming where 2-3 developers can create this together. Maybe you can team up multiple developers on a group of similar projects. Let those mini-teams develop their coding standards in practice and verify the interpretation of the abstract standard by practicing code reviews.

Going from individual to collective code ownership (and a common standard is one element of this) does not happen over night and cannot be decided in a meeting. It is a goal you aim for and achieve by incremental daily improvements.

2
  • And if you don't want to go all the way from strong code ownership to collective code ownership, there's no reason you can't meet somewhere in the middle, as Martin Fowler explains.
    – Eric King
    Dec 24, 2019 at 18:22
  • 1
    Definitely. We are somewhere in between as everybody has an area of special expertise where they do most planned tasks but at least once other person can jump in. And there is a bunch of support tools where there is actually collective ownership
    – Manziel
    Dec 24, 2019 at 18:54
4

Since this is not just about the superficial style, but about the way people write, I strongly suspect that if you force a way of writhing and thinking on everyone, in the form of a "standard", you will stifle the creativity of your team members, and that could kill your team/company. What you need to do is work on turning those shouting matches into something more productive; and learn to hold your strong opinions more loosely.

Don't introduce one big change, you'll meet resistance; instead set a goal ("we want a more unified level of design skill across the team" as opposed to "we want a specific coding style"), and figure out what's a small step to move in that direction. Then when you feel that you got there, figure out the next step. I think you can all agree that places where one person's code needs to interact with another person's code shouldn't be the responsibility of a single person, and that code at those interfaces requires the parties involved to be more careful about the design, and to spend some time to understand the problem of interaction better. So start there; introduce the notion that the code at the boundaries is not decided by, or even owned by a single person. Have them explicitly examine and agree on the dependencies and the "power" relationships between the systems/components.

Another thing you can do is to have them start incorporating some form of pair programming into their routine (during some fraction of the working hours, possibly with some rotation scheme), in a way that's not too disruptive (you'll have to figure out what that means with respect to the way you do work now). This way, they can pick up skills from each other, and "battle it out" among themselves when it comes to how to design the code; because unlike in a heated argument in a meeting, they will be solving real problems and so they would be able to challenge each other's ideas, and evaluate and try them out in context. This would also help with your problem where nobody really understands anyone's code but their own.

P.S. Since you are just starting with TDD, I wouldn't force that on the team either. Instead, I would find a way for them to practice it on some side project, and then to incorporate it slowly into their everyday job, because, IMO, TDD requires a certain amount of practice and a certain level of (test-writing & design) skill before it can be used effectively. Otherwise, they could end up writing a test suite that does not make their lives easier, so they'll eventually start to ignore it and/or develop hostility towards the practice. You could also consider bringing in a good consultant, having more workshops, etc.

2
  • I think I might have worded it too strongly. Our discussions didn't really get very heated. And everyone is OK with backing off when outnumbered. But we did spend a rather large amount of time debating style rather than content, and in most cases instead of settling our differences we merely decided that "hey, we've digressed for too long already; let's just pick something and get back on track". So it was very civil, really.
    – Vilx-
    Dec 23, 2019 at 19:55
  • Also, full blown TDD isn't even on the roadmap yet. The ruling sentiment is that unit tests are good and we should definitely do them, but full-blown TDD (as in, test first, code later) isn't too useful. Tests can be written after code, as long as they are written and do test the code.
    – Vilx-
    Dec 23, 2019 at 19:57
3

Given that there's so few of us and there's just so much work to be done, for most part each developer handles a separate part of the system and doesn't share their work much with other developers. Each has their "domain" so to say.

This is more of a problem than it appears. Having only one developer on a project encourages this sort of overprotectiveness, and has other risks of "bus factor" - if the only person who knows a subsystem is hit by a bus or otherwise suddenly incapacitated or leaves.

In order to behave as a team they need to work as a team, which means more evenly distributing work. This is not at all easy and requires continuous management input, but is worthwhile in the long run.

While doing this and reviewing each other's work, they will end up discussing the details - but pairwise, rather than in a big group setting. That should encourage a consensus to emerge. Unless there's a "most unreasonable" person trying to impose their will.

1
  • The management is well aware of the bus factor and would love nothing more than to improve that (and they have tried), but the reality is that... there's just too much work and too few people, and not enough money to hire more...
    – Vilx-
    Dec 24, 2019 at 10:32
3

If the team culture have been working well with each developer having their own domain and styles, and people are happy with the situation, then don't try to change that. If this isolation isn't a problem, don't make it a problem.

Make it so that following local standards is the general rule. The leader for each domain should decide what the standards for their domain is, and if people are working outside their domain, they should respect the local domain leader's coding style.

This isn't to say that you should not have a coding standard. Some coding standards are going to be fairly straightforward and have widespread agreements, so do standardise those. But for the more contentious ones, don't try to unify everyone's opinions.

1

It's not clear why the coding style matters to you. Is it because it provoked disagreement thus wasting time and creating friction, or is it because it's causing problems in the running of the software, or is it because it makes it hard to write/maintain the software?

So, the first thing to do is work out the why or the what and the how will likely elude you (though all of the other answers here have good ideas about things you should be doing).

What I would probably do in this situation is:

  • write to interfaces
  • write integration tests that confirm the software works when using these interfaces
  • document my work thoroughly
  • do this all for my work only

Until you get power through consensus or power through authority like that of a manager, the only way you can do things is by example. Anyone touching your work will now have to use the specs you provided, which will lead to less errors in your work thus proving its own point. You gain authority this way (the best way).

Ultimately, if the interfaces spew out the correct stuff when given the expected inputs, why do you care if the code has 3 classes or 5? Because you can't read their stuff or you don't like it? You need better reasons to try and enforce a style than what you have.

Finally, if you don't have specs/tests then it doesn't matter if you gave a monkey a typewriter to code with and you don't like its style, you've got bigger problems.

0
0

In addition to suggestions here, I strongly recommend you use screen sharing to do some pair programming sessions so that developers can work together. NOT all day every day, just a few hours a week of working as a pair and mixing up who pairs with whom will have tremendous payoff in terms of normalizing practices and sharing tips with each other.

0

Robert's answer is great, but there's a bit more that can be said about your technical designs' impact on teamwork.

unit tests. (Yup, we're one of the people not doing those yet...)

Unit tests aren't... necessary, if you aren't using OO. If you are using OO, tests help to warn you when your code breaks. But the tests are subject to the same rules as any other code, and they can break unexpectedly in the same ways that regular code can.

If you're using a more functional style instead of OO, unit tests just cease to exist. If they existed, they would look exactly like copy-pasting your code, at which point you just don't do it, so you can keep your code small and readable. See below for an example.

Do I split this code into 3 classes or just one? 5 tiny files or 1 gigantic one? Abstract it away with interfaces and factories, or call it directly? Getters and setters or naked fields?

You might find yourself in arguments over these, but you need to find one right answer. Interfaces, abstraction, factories, classes, getters, and setters are all arbitrary concepts that sound good, but having to understand how they work, and how they fit in your software, is developer overhead. The overhead makes developers want to find new solutions so that they don't spend so long solving puzzles. As these concepts don't have any measurable benefits, you can skip them completely to make your code easier to read and manipulate.

Getting out of these arguments can be tricky. Get good at listening, asking the right questions, making your questions short, precise, and to the point, and poking holes in designs early. If someone suggests using a factory pattern, you may want to counter with an example that uses less lines of code. If they say something unprovable like 'less code isn't always better' or 'it's not up to standards', then you may want to ask them to explain this, so that they introspect on their idea to find the error. If they fail, they will get confused and frustrated. Let the choice slide, work with it, and try again another time. They'll get better at introspecting each time you ask.

If you can't get the developer to introspect on multiple occasions and they don't seem to be improving, then you have a problem developer. If your entire team is the same way, it's unlikely you'll be able to improve the situation. Accept the arguments, or find different work.

You commented on Robert's answer

we have too much experience and everyone has their own opinion based off that

Based on your descriptions, I don't think you have reached too much experience yet. When you hit that level, you get a consensus with other developers at the same level, not different opinions. The answer stands, and don't assume that you're above it - find someone who can show they know what they're doing, and get them to guide you in the right direction.


On unit tests for functional styles

This isn't very relevant to the original question and is a bit long, so skip this unless you're interested. I've been requested to explain it, so..

A functional server would look like this:

//main
http.Listen((request,response)=>{
    response.write(request.username);
});

And a test might look like:

//test that it returns the right value
var username="billy";
http.request({username:username}, response=>{
    if(response!==username){
        console.error("test failed");
        return -1;
    }
});

But then once my basic server needs to do something else, I'd be breaking the test. Refactoring is necessary, but now I have to do it in two spots.

The test also failed the same way the software did! If username isn't defined, we're trying to read an undefined value and will throw an error. We can write a test for this too, in true red-green fashion:

//red: write the test first which will find undefined values
http.request({}, (response,error)=>{
    if(response){
        console.error("got a valid response, test failed");
        return -1;
    }
    if(error){
        return 0;
    }
});

and then fix the code for the green:

    //main
    http.Listen((request,response, error)=>{
        if(!request.username) error.write("no username");
        else response.write(request.username);
    });

But again, same problem. I just wrote my code twice, made it harder to refactor, added more lines to read, and testing didn't do anything useful.

So when is testing useful? In OO. Testing is a functional style itself, where the function intends to keep program flow stable, while also guiding developers to write smaller, cleaner code.

class server{
    listen(IPerson request,string response){
        response.write(request.username);
    };
} 
interface IPerson{
    string username;
}
class Person{
    string username;
}
class TestPerson inherits IPerson{
    string username{
        get{usernameCheckedCount++; return _username;} 
        set{_username=value;}
    };
    string _username;
    int numTimesUsernameWasChecked=0;   
}
static int TestServer(){
    //setup
    var didTestFail=0;
    var srv=new server();
    var invalidPerson=new TestPerson{username=null};
    var validPerson=new TestPerson{username="billy"};

    //test that an invalid person throws an error
    srv.listen(invalidPerson,(string response, string error)=>{
        if(error==null) didTestFail=1;
    });
    //test that a valid person throws no error, the server checks the username, and the response is the username
    srv.listen(validPerson,(string response, string error)=>{
        if(error != null) didTestFail= 1;
        if(validPerson.numTimesUsernameWasChecked == 0) didTestFail=1;
        if(response != "billy") didTestFail=1;
    });

    //return
    return error;
}

If I hadn't written this with testing in mind, I may very well have started by creating a service, 2 interfaces, and a different dependency injection file before getting started on the work itself, which will certainly make the work much more difficult. It's really hard to keep track of everything!

2
  • 2
    If you're using a more functional style instead of OO, unit tests just cease to exist. care to explain a bit further? Dec 26, 2019 at 11:07
  • Yeah, that one boggles me as well. Bugs can exist in any style of programming. How do automated tests cease to work in some of them? OK, I suppose you might need a different definition of a "unit" - in pure FP it might be a single function; in OOP it might be class; etc - but the concept itself stands as far as I can see.
    – Vilx-
    Dec 26, 2019 at 13:55
0

As far as I understood your question, the pure fact that people have different coding styles and design preferences is not a problem. A problem only arises when

the domains overlap and we need to collaborate

and these styles and preferences collide with each other. Now your plan is to solve that problem by "normalizing" the coding style.

To me, normalizing is a rather strange word in this context, as how would any coding style be more normal than any other one? How are the current coding styles not normal? What defines a normal coding style? Maybe that's because I'm not a native English speaker but your choice of word may already be a hint that you are steering a little bit into a wrong direction here.

People usually develop a coding style that helps them to write better code. As all people are different, sometimes more, sometimes less, their coding styles will be different as well. Of course, you can make up a common coding style and force everyone to use it but that's a little bit like forcing all people to use the same hand for writing, even if though some of them are right-handers whereas other ones are left-handers.

A common coding style is usually a compromise and will suit nobody as good as their own one. Some people will have less trouble with it as the style is close to their preferences, while it will be a real problem for other ones. What I'm basically trying to say here: Forcing a coding style on mature developers will cause them to produce worse code in general, sometimes just a bit worse, sometimes a lot worse, depends on your choice of style. And I'm not sure if the improvements in the overlapping areas will compensate for the loss in all other areas.

What I read in your question is that the fact that everyone has an own code style has never been a serious problem so far in general, I don't know if its clever to make it a serious problem by forcing a common code style for everyone and all the time, you see what I mean?

As for collaboration, immediately a good old design pattern came to my mind:
Program to interfaces, not implementations!

For collaboration, it's a requirement to agree on common interfaces. It's not requirement to agree on a common implementations behind these interfaces. So here is my suggestion:

Leave everyone their favorite coding style but when designing interfaces for the sections where domains overlap (or may overlap in the future), make sure no single person alone will design them. Either design them in a team meeting or make a rule like at least three or four people must work together on every interface. That way you will gain much better interfaces and better interfaces automatically lead to better code. They also answers questions like how to split your problems into pieces or how to name elements and most important, how pieces will interact with each other.

And keep in mind: A good interface can survive for a very long time, since you can always replace the implementation behind it without having to touch any other code. That's why interfaces are more important than implementations, as it's replacing interfaces that is really hard work. However, no matter how good you are, you will make mistakes when designing interfaces all on your own. You will overlook problems, you will overlook missing details and having a couple of extra eyes next to you will really be helpful.

My suggestion basically follows another old developer wisdom:
Never touch a running system. At least not, unless you have to. Forcing a coding style upon everyone would be re-designing your system from scratch, despite the fact that it seems to have been working very well so far, hasn't it? Don't fix the part of the system that isn't broken. Focus only on the actual problem and that is the areas where domains overlap.

Not the answer you're looking for? Browse other questions tagged or ask your own question.