One goal that I think often gets missed out of code review guides is education.
To me, code review is not just there to produce better software, but also better developers.
It's a great place to talk about tradeoffs, alternative approaches, reasoning behind changes, etc.
Maybe less useful in huge projects with many contractors you're never going to work with again, but I've seen the mentoring aspect have great benefits in small teams.
Couldn’t agree more. I am lucky to have worked with two individuals early in my career who would take the time to really help develop team members by way of thoughtful, detailed PR comments. I attribute much of my own growth due to this, and as such I always try to do the same for others. I care deeply about it and consider it to be one of the most important parts of the job. It saddens me that most people (IME) don’t feel the same.
On the other side of the spectrum - code reviews have never, not even one single time, given me any real opportunity to learn something of value. They have only ever been someone else shoving their preferences onto my coding style (for better or worse)
I was shocked at my first job to realize the code reviewer didn’t even read my code, merely glanced over and demanded style changes (which were not consistent from review to review in the slightest)
It really felt more like how you hand your advisor your thesis with obvious easy to fix errors so he doesn’t compulsively decide “well something must need improvement” and make you fix something difficult
From your multiple comments on threads it seems like your ego is strongly attached to your code - if you truly never, not even one time, derived value from a code review, either you always choose to work in toxic environment/s or you're not willing to accept feedback or defend your "coding style" well enough to align colleagues with your opinion. I desperately miss code review feedback at this point in my career after excellent engineers made me question my strong beliefs and empathise with theirs helped me build my path.
I do a lot of mentoring and I’m not sure that code reviews are really the right place for it. Comments in a code review can be fine for small things like “oh you just rewrote a function that already exists over here- maybe you’d like to use that instead”, but those things don’t really matter much in the end.
Teaching people how to design applications is a lot more useful, but code reviews are really not the time and place. For one thing, making someone go back to the beginning when they have a working (if flawed) PR is going to demotivate people and strip away their confidence. It’s also going to really limit your ability to ship quickly. Even if you put those challenges aside, the review experience for GitHub PRs (which I’d guess is how most people are doing reviews) doesn’t allow you to comment on code that wasn’t part of the diff. This makes it really hard to discuss the broader impact of a change i a PR because you can’t add context inline about things someone missed.
After trying and failing to do good mentoring through code reviews I’ve mostly given up. I do use them as a chance to see where people might benefit from some mentoring, but I keep my reviews quick and lightweight. If I don’t see anything wrong with the PR I approve it. Mentoring happens during dedicated time I put on people’s calendars to focus just on mentoring outside of a PR.
Absolutely - I don't see code reviews as a replacement for mentoring, more as something to augment it, or direct future mentoring sessions.
I've had quite a few experiences where I've suggested a change in a code review, and found out that the author lacked knowledge in that area. That's then turned into a future topic to focus on in future mentoring sessions.
Agree about design too, I don't think code review is really the place for that, since it's often a bit late by then (plus it's hard to get a sense of the overall intent and architecture from code chagnes alone).
It's a shame it doesn't happen formally more often, but one place I worked in the past always had a separate design review (which needed at least one person to approve) before coding could even start. This was fairly informal, but caught a few major potential design problems early on.
Your point about the inherent conflict between educating juniors & having lots of contractors is an important one. The most messed up teams I worked for were 1/2 (junior) contractors and this was a big issue. You basically have a team of bad developers you don't really mentor & expect to get better. As to the why, you can talk to management. The senior engineers lost that fight.
Agreed. And, sadly, besides providing zero education, some code reviews even turn into spaces of abuse of political power when conducted by unprepared reviewers.
I've been unfortunate to work with one of those, some years ago. I decided to pretend I enjoyed the style as soon as I realized she suffered from a fragile sense of authority. In hindsight, that doesn't seem to have been a good decision.
It was clear she felt affronted by any deviations from the intended standard. But the feedback mostly comprised extremely vague, subjective, non-actionable ad hoc prescriptions. I carefully read through my reviews and the ones received by my colleagues, but was unable to devise any pattern. Ultimately I decided to simply code as fast as possible so that I could collect the feedback earlier and get rid of the damn thing.
That way I could kind of cope, but after some months I was really burned out and struggling to sustain any interest about my tasks.
Additionally, the Education is both ways - the reviewer is also getting educated along the way, especially from new developers who have a more fresh knowledge of things.
After 30+ years, I kind of reverse the pyramid, honestly. I look for readable, maintainable code, and try to make sure the writer is doing appropriate testing. But whether or not it works and is correct, that's not my job, that's the developer's job. I know developers that will spend days on a code review, which is a horrible waste of time. CI/CD and your deployment followup structure (canaries, monitors, gates, etc) should be catching any significant issues the vast majority of the time, and if it isn't, you need to spend time there.
My main concern as a reviewer is to make sure future people can understand the code when the inevitable modification comes along.
Things are different if the writer and reviewer are in a mentorship relationship, but even still, if you are only engaging with the code as a mentor when the PR hits, you're messing up the mentorship!
“ I look for readable, maintainable code, and try to make sure the writer is doing appropriate testing. But whether or not it works and is correct, that's not my job, that's the developer's job.”
And I personally refuse to work with this style of workflow anymore
Does the code do what it needs? That’s objective and it either does or doesn’t. I can manage that kind of review.
Does the code look pretty enough? This is subjective and guarantees i’ll waste hours of my life every month bc someone else felt anxious/neurotic/masochistic and wanted to dump a chore on me.
No thanks, if you have style guidelines i’ll follow them. I’m not your whipping boy that will ask how high when you say jump.
The fact that something is subjective does not mean that it is useless.
IMHO, writing maintainable code requires a lot of experience, which one cannot expect from all junior developers. Requiring style guidelines for everything is a bit pedantic, and does not even make sense when you stride in to new territories. The latter is increasingly becoming the only part of software development where I would like to go.
If all code and requirements would be so objective and so simple, then I'm afraid you'll soon be replaced by an AI agent :)
Readability is not "prettiness", it's minimizing the effort as much as feasible to understand what the code is doing. I don't care what lines the braces are on.
From what I've seen so far, people love debating code style.
The main problem is that those programmers mostly care about superficial syntax and don't actually care about code readability or design. They just go for the lowest hanging fruit and criticise variable naming or whitespace because it is easy; they don't talk about the object graph or the event lifecycle of the program, because that's hard.
A very common thing I see nowadays is a forced adoption of gofmt/black/whatever, hoping it would solve the obsession with the formatting. However, this just locks in a (often substandard) coding style and removes any kind of personality from the code. This is good if you are a manager trying to treat your employees as fungible units of work, but is bad for actually maintaining a codebase. Also, it doesn't stop obsession with style, it just forces a specific style on everyone which most of the time, everyone slightly hates.
I found your comment insightful until the "(often substandard)" part. This betrays the rest of your comment. You start out saying how arguing specific indentation style is superficial but then betray this with that comment. If it truly is superficial then it does not matter what you lock in as long as you lock it in.
As with many things it's not actually a technical problem. It's a social problem. And it can work. Where I am right now for example we do have formatting locked in and enforced by linters. And it's not an issue there's no debate about it in regular PRs at all. The code formatter adjusts it and that's that. If you want to change it, create a PR that changes the rules and put a cross section of developers on said PR to discuss it. Happens from time to time but not often and works well.
I say "often substandard".... because, honestly, it just is. For example, breaking long logging lines into multiple lines decreases readability, but the formatter doesn't care because it only sees the max. line length and that's it.
Same with breaking long function definitions: it doesn't consider what makes sense or what is readable, it just messes the code up. Similarly, some projects differentiate between single and double quotes (e.g. single quotes for data, double quotes for userstrings), and this just drives a tank over it.
Or completely disallow tabs, even when it's necessary for accessibility, like with screen readers (not even PEP8 enforces spaces, but black decides to without a configuration option)
Same with whitespace: it refuses you to add more than 1 (or 2, can't remember) empty lines inside a function, even when it would be more readable to space it out.
I heavily disagree with the assumption that consistency is better than readability. I believe that people can be civilised and not change each other's code just for the sake of formatting nitpicks, only when they actually edit the code.
If it’s configurable, then it’s never settled. People will find reasons to want to change it, or someone new will come in that used a better config at their last job.
> People will find reasons to want to change it, or someone new will come in that used a better config at their last job.
This is fine. Discussing code style once in a while is not a problem. What's a problem is discussing it at every PR.
Having to endure through asinine style errors that no one in your team chose or even agrees with is worse than spending a couple hours every 6 months discussing a rule or two.
That just means your coworkers are anti-social... this masks the problem but the real solution is educating people so they won't be anti-social anymore and stop nitpicking about others' style.
Teaching people to “just don’t be anti-social” is not the purview of people on a team trying to get work done, nor is it even possible in many or most cases.
The lack of debate over line-to-line code style (because it is enforced by a standard formatter) is one of the reasons I still enjoy using Go, and that's despite the fact that it doesn't use what was my preferred C-era brace style.
Just absolutely eliminating it as something to worry or argue about is something a lot more languages could benefit from, IMO.
And yes, while other languages have third-party formatting and linting tools that can enforce an agreed-upon standard, in the real world that just never works out as well (once a codebase grows beyond a couple of contributors) as having it be truly standardized in the language itself.
> in the real world that just never works out as well (once a codebase grows beyond a couple of contributors)
Can you elaborate on this? Once you have a linter/formatter in your CI loop, how does it stop working just because you have more contributors?
My main project at work uses Elixir - which has a built in formatter - and Javascript - which doesn't. We use eslint for Javascript and haven't touched its config in years as devs come and go.
I think go has the benefit of the syntax being simple enough that not as much debate is needed. That is a legitimate feature of the language, and why not enjoy it. This comes already from the design of the language, I would say.
I'm comparing to Rust, and I'm not surprised that there are more different ways to format Rust and not always a way to make everyone happy.
> From what I've seen so far, people love debating code style.
In my experience with people who seemed to confirm this, the opposite turned out to be true. I spent an inordinate amount of time configuring linting and formatting to suit their tastes, to minimize any formatting subjectivity in code review. Their reviews got a lot more substantive and valuable pretty much immediately because their formatting concerns were evidently close enough to solved that they could pay attention to what the code actually did.
I’ve had similar success adopting formatting that no one likes just because it’s opinionated and no one gets to dispute it because they couldn’t if they wanted to without diverting resources to a new tool. Now we’re all unhappy with the formatting and still happy to be reviewing actual substance.
When the bad formatting gets in the way, it’s trivial to ask the question “is this just a whitespace change?” and everyone groans for a second, agrees that it is, and gets on with life.
And when there’s any remaining style considerations (oh you like, or don’t like, reduce?) that’s a much smaller space of style concerns to negotiate and settle.
I think this is just accommodating anti-social behaviour, IMO. I think the correct choice of action is simply rooting the problem out by penalising comments about style on code review, so they focus on the substance of the code instead. Yes, this will require a period of uncomfortability while everyone gets used to each other's varied code styles, but it promotes a much more healthier atmosphere than the "suck it up" approach of unifying all code. That doesn't solve the problem, that just masks it by putting the code through a shredder so style gets lost.
I have a ton of code style preferences that I’m thrilled to put on the back burner to accomplish really much more important things with my team. I can express my individuality and let my whitespace and brace freak flag fly on personal projects, and I can even have more energy to get those adopted into core work if I want to, because my daily work isn’t burdened by formatting preference taking away from much more important concerns like whether the thing actually works, has acceptable performance and usability and accessibility characteristics, and can be maintained on a continuous basis.
It’s not that anyone else has felt stifled by this either. Everyone everyone contributing has welcomed the automated formatting dictator. We’ve all felt the benefit of not focusing review on stuff that doesn’t impact users.
If you really want to express yourself in code formatting, go wild on your own time and enjoy the free time you have for it by not putting it in the middle of otherwise meaningful work.
A consistent style is useful. It doesn't matter much what it is, but consistency means you can jump into unfamiliar code and not get misled by a different style that makes some block not appear how it actually functions.
Code should not have personality. If you're working in a massive code base and people can tell you wrote code, that's probably an indictment on your code more than whatever style the company has chosen to use. Homogeneous code bases are the goal.
Only if you are an employer who wants to treat programmers interchangeably..... as a programmer, you have zero reason to do that. If all the codebase is homogenous, you can be replaced way easier, which is probably not what you want.
If your code style is what gives you the feeling of security at work… Well that’s a bit sad and I’m not convinced a linter can be blamed for anyone losing their jobs.
No, mostly not. Doing my job properly gives me job security for the most part - but this is one of the things where I don't see any benefit for yourself, there is only a benefit for your employer. Of course, I'm not advocating writing cryptic undocumented obfuscated mess (that's just plain unethical, even if it's "good" for you), but coding in a "standard" style just benefits your employer, not you. It just means you are more easily replaceable without any benefit for you. You won't learn more from doing it, you won't gain any skills or anything.
> It just means you are more easily replaceable without any benefit for you
What does this even mean?
Companies don’t (well, shouldn’t) hire developers to just write code. They hire them to solve problems. I’m not going to more easily replaced because my code follows the same styles as others, if I am the one solving the problems they have.
Yes, but others can also solve those problems. And if you make your code really standards-comforming, management will see that they could onboard another programmer really quickly. This is what you want to avoid.
Not to be mean or rude, but if we had a developer that was refusing to adhere to our standards, they not last long. Being able to produce code in agreed upon standards is table stakes.
You would not last long in my team. When you “forget” the standard, you would have to rewrite your code. In the end you would perform a lot worse than other team members.
Anyone should be able to follow coding standards, you do not have to be smart for that. You just have to be able to put aside your ego and follow what the team is doing. I am a lot more forgiving to people making mistakes than to people forgetting the coding style.
And anyhow, I enforce the code style by making sure the code simply does not build if you do not follow it.
I definitely agree that Black (which I love) is pretty overrated in the number of problems people think it will solve for them. But I just don't get this view
> removes any kind of personality from the code
What kind of personality comes from things like pefering " over '; having tabs over spaces; having n spaces between class methods?
For me, those things are essentially meaningless but nice to have consistency on. Code personality, and a large part of readability comes from things like:
- variable / class names
- functional vs oop problem solving
- type use
- custom data structures or not etc
None of those have are locked down or touched by a lint tool like Black. Use it or don't, but you'll have plenty of personality in your code either way.
That's not the interesting "personality". I think the formatting tools bring in something completely different: code layouts that you would never write by hand.
I don't like that. Code should be a transparent interface, you write something and it becomes part of the program. With code formatting it becomes, you input something into the source code, you transform it with the tool, and then it's done. We sort of lose the brain-hand-program connection.
As an example I randomly found this code snippet of rust imports. To reproduce this by hand would be like writing JSON by hand - I'm referring to the nesting, bracketing and sorting. This is the lack of personality: no longer writing code by hand, but with tools.
use core::fmt;
use std::{
alloc::{GlobalAlloc, System},
cell::UnsafeCell,
cmp,
fmt::Write,
marker::PhantomData,
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
time::Duration,
};
use serde::{
de::{self, SeqAccess, Visitor},
Deserialize, Deserializer, Serialize,
};
"Consistency is the most important" seems to be an axiom...
"having n spaces between class methods" is definitely *not* meaningless. It affects code readability and black has very opionated (and often inappropriate) ideas about what counts as "readable" whitespace. When you raise that problem with people, they always say "well too bad, you should write shorter functions instead", which just introduces another layer of opinionatedness.
>A very common thing I see nowadays is a forced adoption of gofmt/black/whatever, hoping it would solve the obsession with the formatting. However, this just locks in a (often substandard) coding style and removes any kind of personality from the code. This is good if you are a manager trying to treat your employees as fungible units of work, but is bad for actually maintaining a codebase. Also, it doesn't stop obsession with style, it just forces a specific style on everyone which most of the time, everyone slightly hates.
This is so wrong. There is no reason to not have a standart for formatting code when working in a team.
There are good reasons for not having a standard code formatting style in a team. They might be ones you don't agree with, but saying there are no good reasons is IMO a bit disingenuous. With mature team members, it's perfectly possible to respect each other's style and if executed well, it can have advantages such as being able to very quickly identify who wrote something (who to pester about something), making readability decisions locally instead of globally and benefits like that.
Of course, that requires a team with emotional maturity, which is I get, not very common in the software industry.
Reading this and your other comments I have two very conflicting vibes that I:
1. Hard disagree on the idea of multiple styles in a single codebase and think being able to identify writers of code by style rather than version control sounds like madness incarnate.
2. Massively want to read a blog post or something from you about your ideas on code style. Sounds like you have some pretty valid and well thought out ideas justying a philosophy of code style I've never heard supported before (I think normally disparate code styles in a code base would get interpreted as a code smell rather than a positive)
If style didn't matter at all, we'd be writing assembly or bytecode. But we don't, because code is written for humans most importantly. Reducing the code down to basically the AST makes it lose important semantic meaning; which defeats the purpose of high-level languages.
Hi! I don't have a website or a blogpost or anything, to be honest. If you want to chat about it, I'm happy to - my Discord is on my profile, my email is prisznyak.zsombor@gmail.com , or if you want to communicate with some other platform, let me know.
I suppose you never encountered git conflicts caused by differences in the autoformatter settings.
Or a lot of changes without reason, making it more time consuming to find the real change in the commit diff log.
I don't mind differences in code style. There is a standart called editorconfig, which IDEs support, which makes code style rules IDE independent.
Actually if you have people with experience and professionals, they don't care about a specific code style rule, they don't care if they switch the code style from project to project, they just want a standart set of rules for the whole team so that there are no issues caused by different formatting styles.
> it just forces a specific style on everyone which most of the time, everyone slightly hates.
Just stop caring about useless stuff, gee… this is what the code review pyramid about, focus your efforts and care on important stuff, not irrelevant details.
Auto formatted code is the best thing that happened in the last few years. I don’t care that it doesn’t show the « personality of the dev who wrote it », I want consistent code.
> However, this just locks in a (often substandard) coding style and removes any kind of personality from the code. This is good if you are a manager trying to treat your employees as fungible units of work, but is bad for actually maintaining a codebase.
Is it bad, though? Having most of the code in the codebase look and read consistently feels like a good thing, even if that's not the style that you'd personally prefer. Especially if the formatter does its thing whenever you save a file.
I mean, you are right.... But as programmers, shouldn't you try to be less replaceable? Have unique skills or methods which can be valued, instead of being a generic hotswappable code monkey.
I don't really understand your logic - why is management right for removing personality? Of course, from a programmer's perspective, not from the manager's perspective.
Being able to use generic hotswappable code monkeys would be the wet dream of corporate executives. Imagine being able to scale or shrink your dev work force on demand, as if we were AWS instances. Actually this is the selling point of companies like Accenture or Globant, no wonder they have been so successful.
From a dev pov, making sure you are easily replaceable means that you could also find a new job easily. So it’s not all bad.
I find this too concrete to be useful. I have a more principals / tradeoffs approach to code review:
Areas:
- Readability - how understandable the code is
- Maintainability - how the code enables the project to evolve
- Risk - security, regulatory and other vulnerabilities
- Correctness - whether the code does what you intended
- Robustness - how well the code handles unintended circumstances
- Performance - how resource efficient the code is
All of the above areas need to be considered within the context of the project and individual team members.
## Readability
Code is readable if it meets your expectations and surprises you only when there's something you don't yet know about the technology. We expect code to look like the code around it. To follow most, if not all of the technologies idioms. To use clear and informative names. To explain why deviance exists. To be as abstract as the concepts being abstracted are crystallised.
## Maintainability
Maintainability is the code's relationship with the wider team and time. You should be able to learn all you need to know to change, test and deploy code. You should be confident that any changes you make will not cause unintended changes elsewhere in the code. To make a change, you should have to touch as little code as the concepts being changed are crystallised. You should be able to debug an issue as easily as, the system the issue is in, is old. You should be able to track down and alter the changes that introduce a bug quickly. You should be able to learn why a change exists.
## Risk
Code is low risk if it accounts for, and where possible, mitigates the various risks to a project.
## Correctness
Correct code does as you expect and surprises you only when there something about the system you didn't yet understand. Validating correct code is as automated as the validations are frequent. Correct code is only as complicated as the concepts it models. Correct code is only as abstract as the concepts it models are crystallised.
## Robustness
Robust code handles unexpected circumstances safely, quickly and predictably. Validating robust code is as automated as the validations are frequent and as comprehensive as the failures are risky.
## Performance
Performant code is as resource efficient as the resources are expensive, but also as efficient as the development costs are cheap.
Ehhhhh yeah kinda? Yes, you should spend most of your time on "API Semantics" (what does it look like using this code?), and you should spend a lot less time on "how are the tests?"
but, for example,
Writing good tests massively contributes to good implementation details and API semantics; and test code is also code that needs to be reviewed under more-or-less the same criteria as the rest.
Also - documentation (or, legibility, if you're onboard with self-documenting code) can be more important than either implementation details, and even API semantics, as it can define whether the entire work is useable or maintainable.
I might say instead:
- What will it be like reviewing this code? (style, test coverage, etc - do I have to worry about spotting typos, and other stupid stuff?)
- What will be like debugging this code? (patterns, logging, etc - which might handled by a framework)
- What will it be like altering this code? (documentation/legibility, implementation details, etc - when the business needs change or grow)
- What will it be like using this code? (API semantics, API docs - when I go to build something on top of this)
And then yeah; the top should be entirely automated, and you should (generally) spend most of your time on the bottom.
My own "but" is shorter: the idea is fine, except code review also "should", per the prevailing wisdom, happen on small, focused changes. But that deep in the woods, style and testing is pretty much all you can talk about. There isn't much use in starting a discussion about API semantics on a commit that implements a stub of one of its endpoints or sth.
I might hold an unpopular view here but I do not like reviewing small focused changes.
When a feature is implemented I preferably want the entire thing before me when reviewing. Otherwise I find it hard to keep track of everything that has happened.
Not to mention that the small focused changes might be reviewed by different people leaving only the implementer in full knowledge of everything that was done.
Peer/Assembly programming help but if you do peer/assembly programming reviews are mostly a waste of time (especially for assembly programming).
At a former team, we went from spending quite a bit of time on code style comments and disagreements to spending no time at all on it, with the simple act of making the code linter a breaking step in our CI build, and deciding no review will start until the build is green.
We had to adjust our linter settings here and there - but it was still super efficient for everyone's time compared to what we had before...
If you are reviewing implementation semantics after someone has already coded a feature, I'd say it's too late. You might catch issues, but the loss of goodwill and wasted productivity will make everyone hate code review.
I think this pyramid is accurate but better framed as a "review pyramid". Catch semantic issues in an earlier phase (informal discussion or rfc-style proposal document).
That really depends on the size of the feature, but imho we should be reducing the need for meetings/reviews, not adding more. Forcing design reviews just slows people down. Jeffy B's "communication is terrible" are words to live by if you want fast moving engineers.
Sure, but the price is worth it. If we have a design session before you start coding, it definitely will make the feature go “late” by an amount of time proportional to the design session… which is less time than the required to fix the design after the PR is open.
“Fast moving engineers”: I can only imagine managers advocating for that. As an engineer, I can only but express disdain for such a mentality.
I disagree, as someone who has been both an engineer and manager, if you want feedback one on one from someone who you know would be valuable, always go for it, but official design reviews end up including many people who don’t matter once it becomes a required process. The people who most love taking your time for design reviews tend to be architecture astronauts, not fully grokking the real nutty gritty or caring about things that don’t matter.
Your relatively short comment caught my interest and I'd like to learn more about your thoughts.
As, IMHO it depends (as so often). Code review is commonly the place that puts the current style under test (hence automated otherwise you don't have the results during review) and under review.
E.g. Code review finds out if a code-style is missing, incomplete or even outright wrong.
If that approach is taken, passing tests and code-style must be in there, otherwise it is not useful to argue about violations. From my understanding I would therefore see those results be available during review.
It may also be that there is code-style for automated test-code, and that code part of the review. Without taken this into context of the code-review, I miss a bit the boundary of your comment.
Could you elaborate a bit where you draw the line and why? E.g. I can imagine there is a benefit to keep a distinct context for code-reviews so that they are still practically feasible and those parts that need further adoption are put into a different phase, like steps before (preparation of the current increment) or after (preparation of next increments) the code-review itself.
Style formatting should be automated on level of IDE or other CI tooling.
Discussing or fixing code style is huge waste of time and styling can be automated. Yes it will be broken in some edge cases but for me that is acceptable because my goal is to deliver the feature and not to make "perfectly aligned code". Aligning code by hand is waste of time, thinking about aligning code is waste of time. Formatting should be in configuration and you format whole file at once and never do stuff by hand.
Passing tests is also easy to automate when creating PR, CI should run tests and tell that to person creating PR - hey tests failed, fix it and then after they pass you can open PR, not a fellow developer because that is waste of time for everyone.
I did not write "tests should not be there" only "are all tests passing?" is not part of code review for me, it is something that is there before code review even begins.
So checking if there are proper tests and if tests make sense is part of code review.
Thanks, that better helped me to understand your comment. Additionally I was not aware if that was with pull-requests or more with regular code-reviews as part of the development process. That was an additional insight, thanks for sharing.
Nevertheless, things can be different, e.g. if CI passes, it merges already, no need to have a request for merge, which are often a blocker to keep a steady development flow. Naturally, this benefits from tests that are already run during development, if not driving the development.
And I didn't read you're against testing nor aligning white space and comments at all, more the localization. Actually the points you raise are looking important to me, because if alignment comes in late, this can cause a lot of erosion, which can hinder any review if not even provoke merge conflicts which are stopping the process quite early and require re-iteration.
Your approach should also work towards non-release blocking code reviews, a property I personally like, as I've seen teams struggle with code reviews, becoming more and more of a burden, even after practicing it has found its way. But that is only a subjective comment of mine, every project is different, which makes it an interesting topic for me.
I don't care about code style. Granted, I work in a small team, but I work on a project with someone with a different style. All I ask is that we respect each other's style. And that he puts braces around single statement branches.
What's surprising to me in the article is the pyramid style. In all business school and psychology texts, the top of the pyramid is the most important one. Here it's the least important.
There should be no "someone with other style working on project" - project should have its style and dev should adjust to that style.
Same with joining new team, one might have preferences but you leave them at the door and format code with whatever already is there.
There is tooling where you can also have formatting configuration per repo so if you use IDE it would take that config and format it for you in "project style".
You'll get used to reading diverse code in a fairly short time. "Coherent" and "easier to read" also has the downside of lowering the attention you have when reading the code, so you'll have a harder time catching obvious mistakes because "it looks correct".
And again, being against personal flair only makes sense if you are a manager trying to treat programmers interchangeably, but as a programmer, your interests are increasing personal flavour in code so you are less replaceable.
The real problem here is the lack of documentation though - like, if you use non-standard tooling or style or decisions, the burden should be on you to document them. Sounds like in this case this hasn't happened - which is really sad, but not much different from awful (but standards-conforming) codebases with zero documentation.
If he actually documented his abbreviations, mnemonics, style and architecture, you would have most likely appreciated it instead of cursing it, I think.
This is true, but if he had documented anything then he would have become significantly more replaceable.
So replaceable in fact that the company wouldn't have had to hire me at great expense and could have got a normal developer in to take over and continue.
Maybe I'm just extremely jaded since my job is fixing messes left by people with more style than sense but I see striving for uniqueness to be a flaw in an engineer.
Fair enough. But for him (and you) it worked out great! He got some job security, and you got a job by fixing the mess. If there wasn't a mess, you wouldn't have this job:)
I consider code and project property of a company I work for. If they have dress code or code style it is for them to set it up. I might propose some changes that fit me but most of the time "when in Rome do as the Romans do".
Sure, but we are the Romans. The code base was a shambles. At least things runs without failing now. And management considers features and selling more important than maintenance, so the best we can do is keep it simple.
> If they have dress code or code style it is for them to set it up.
I also chose this company because they don't. A dress code, what power-hungry lunacy is that?
Granted, I wrote that sloppily. The bottom forms the basics, the rest is (supposed to be) built upon that and we're supposed to strive for the top. That's Maslov's idea. But Code Style isn't built on top of unit tests, nor is it code review perfection. A pyramid simply isn't the right graphic for OP's ideas.
Formatting is not the only component of code style, it's just the part that can easily be automated. Anytime there is more than one way to do something, and both options are correct and produce the same result, the choice between them is likely to be a matter of taste or style. For example, using a loop instead of using a map function. You can automate some of that stuff, but never all of it.
I write and review medical software. When I'm reviewing code the most important question is, "Is it correct?" If it's not, then the review simply isn't approved (obviously).
Anything that makes it harder to determine correctness is a strike against the code. This can include anything from high level organization, to comments, to the naming of variables, to the use of whitespace. For example, with respect to variable names, if you're implementing something that has to conform to the DICOM standard, then you should consider following their nomenclature, even if it may not be what you'd normally use in other contexts.
Aside from correctness issues, I'll make anything from "strong recommendations" to "mild suggestions". (In theory, any of these could cause me not to approve a change, but the closer we get to "mild suggestion" the less likely that is.) Examples might include:
- You're trying to do something with X. I'm a domain expert in X and while your code is technically correct it's not idiomatic. I suggest doing it this other way instead.
- You added a public function to a library, but it will fail if the String argument isn't a valid Photometric Interpretation. Yes, I see that you only call it with valid values, but since it's public anybody can now call it, and they may not be so diligent. How about making it an Enum?
- You have a loop that, superficially, looks like it's calculating Bar, but upon closer examination it's (correctly) calculating Bar'. How about adding a comment stating this, so that no one is tempted to (incorrectly) "fix" it?
- You've implemented a function that does Y. All our code links with the Foo library, which happens to have a function that does Y. How about using that instead?
- Your log message makes sense if you're reading the surrounding code, but someone from support seeing it in the log file won't know what to make of it. How about including this contextual information in it?
- In the loop that you added, your indentation is inconsistent with the rest of the function. How about making it the same?
- Why do you have four blanks lines in the middle of your function?
In my own code I'm pretty anal about formatting, comments, log messages, use of whitespace, etc. I'm definitely less harsh when reviewing other people's code, but I have to admit that when I see sloppy formatting, grammatical errors or gratuitous use of whitespace, I'm on the alert for sloppiness in other areas, such as design and implementation.
Tests should mostly be integration, not unit. Unit tests tie you down to the specific implementation, which means changing the implementation becomes much more onerous. Integration tests have much more value, give you higher confidence that the code does what it claims, and is easier to code review. https://kentcdodds.com/blog/write-tests
Eh, I disagree. I find the traditional test pyramid a suitable model still.
Sure, unit tests require much more maintenance than higher level tests, but they give you confidence that the smallest parts of the codebase work as intended. They're the ones that test all sorts of failure scenarios and edge cases, which is typically not the purpose of integration tests. They also should be inexpensive to run, simple to write, and require minimal setup.
Unit tests should also give you immediate feedback that something went wrong, by pinpointing the exact component that failed. In contrast, integration tests might happily pass, as they're not granular enough to cover all code paths. Integration tests focus on, well, that the integration between components is working as expected.
So I still insist on having mostly unit tests, many integration tests, and some E2E tests. Doing it otherwise because it saves you maintenance efforts will haunt you in the long run.
Most people don't have a rather obvious interface on their units to tests. If you are implementing one of the CS3025 (your university probably used a different number scheme) algorithms, then the interface is obvious and worth a unit tests. Even if there is some doubt (rare), you will have so many users that is isn't possible to change anymore anyway.
Most of us are writing glue code that isn't as clear what the api should be. Unit tests just get in the way of refactoring when requirements change.
On the one hand, I agree that writing unit tests before the design has settled and the interfaces are stable can be frustrating. Many people find TDD a chore because of this.
But if you delay adding unit tests until after the interfaces are more or less stable, it gives you higher confidence to refactor the implementation, which is invaluable.
On the other hand, the benefit of TDD is that it helps with the actual API design. You immediately experience the API as a user, and this process drives the implementation, and ensures you end up with a friendly and testable API. If you add tests after the design, then you might have a more difficult time refactoring the implementation to make it testable, and the DX could suffer.
So I see both sides of the argument, but in either case, unit tests are very important, and I wouldn't avoid them just because they're a chore to maintain.
TDD gives you a testable interface, but otherwise doesn't help think what the api should be.
You can so TDD using only integration tests. This means tests are not added after the design. It only means that your individual functions don't have tests. I do this allowed the time, it makes refactoring easier.
The most common objection: how do you know what failed? Turns out to be a non issues, since I make my tests fast I can run them often and thus the failure has an obvious cause: the last thing I changed.
Unit tests help in a very specific circumstance - when you're testing complex calculations or logical code with simple inputs or outputs.
Some projects have almost zero code like this.
In all other situations they suck. When they fail it doesnt necessarily mean anything is wrong and vice versa. It just mirrors a portion of the code. It's a lot of expensive maintenance with no payoff.
In contrast integration tests are usually much more meaningful - they will typically test a real system or user interaction.
I had that mindset for a while, just to fall into enough issues that I had to balance it again.
Integration tests are important for features, unit tests stay important for code blocks. My realization was that a ton of what we wrote aren't features, but blocks.
For instance, let's imagine an API that build an invoice. Where do you put the VAT calculation test ? Do you jam your 100+ VAT test cases in the integrations tests, or do you unit test the VAT calculation in isolation and only cover the main cases in the integration ones ?
Same for name display, invoice number generation, Invoice items fetching etc. If you want a decent coverage and also document/properly test the edge cases, unit test will easily be half or more of your whole test volume. And of course you'll want to reuse there blocks, so make sure they're rock solid, so pay even more attention to tests.
Unit tests just help to make sure that the units you have right now are doing their jobs. Obviously if you change what units you have or how responsibilities are allocated among them then you will need corresponding changes in the unit tests.
I don't see why. There are some type of code where I think unit testing is very beneficial and other types of code where I'd probably prefer a different style of testing. But for code where unit testing helps I find it useful independent of any of other types of testing that may or may not be happening as well.
I recently received feedback from my manager to take time out to review teammate's PR and design docs. For context, I've always been under-confident and scared to review and feel like I fail to add quality feedback to other people's code and design. I'll use this article as a guideline for my next PR review!
Has anyone else been in the same situation as I? How did you overcome it?
You may just have a keen level of self-awareness. If you don't have any suggestions, you can just say so. As you gain experience this will likely change, and you will have more to offer.
Don't be fooled by your peers that look for superficial things to comment on. There are many people with the same amount of knowledge as you, but who are less self-aware and are adding noise the system, because they are scared of being perceived as incompetent.
Find someone who has given you meaningful feedback during code/design review, and see what kind of comments they leave on other people's work. If you don't understand one of their suggestions ask them about it.
One of the most valuable feedback comments you can leave on a review is "hey I found this part confusing, can you add a comment?". I think sometimes people are afraid to admit something like that, but obviously if you're confused by it now, in-context, it will not be easier to understand in 16 months. In general you don't have to provide super clever bug catches, often a second set of eyes is all that's needed. Its surprisingly easy to leave really stupid stuff in your diffs, because you gloss over them, thinking you remember exactly what's in there. As long as you don't make any comments about style, or make a ton of work for them, people are usually very receptive. People enjoy shipping things that are quality, so if you give them an excuse they don't mind adding some comments or trying to merge 2 samey segments.
Code review is just the last line of defense to make sure people don't merge things that will create problems for everyone else.
Good engineers figure out the important stuff before anyone writes a line of code. The APIs, the architecture, high-level component names. It's all been talked over. When a good engineer reviews another good engineer's pull request, they're just checking that what is delivered is an implementation of what is expected.
Code review is a road block for people who don't do the above. It causes them to bump into someone who knows what's up.
Everything else that gets argued about: local variable names, formatting, equivalent ways to express the same thing. It's not important. I don't know if it's actually confusion about what matters, or if it's a desire to make people jump through hoops, or maybe boredom?
I mostly agree with you, but often, especially with greenfield projects and features, the implementation details are not clear until someone spends the time to dig into the codebase, and deliver a proof-of-concept. This can then drive the design discussion to get everyone on the same page. And even then, the design might need to be changed if it happens that some things weren't taken into consideration, or new specs are communicated and priorities change.
It's also worth not spending too much time on creating the perfect design upfront, since it might change during development. The whole waterfall vs. agile situation. An initial proposal document helps with avoiding lengthy discussions and friction during code reviews, but it also takes a lot of effort to produce, and even then, reviews can be difficult for many reasons.
> I don't know if it's actually confusion about what matters, or if it's a desire to make people jump through hoops, or maybe boredom?
My theory is that sometimes it's a need to say _something_ to prove that you've read the code. Also, egos are often involved, and some reviewers have the need to demonstrate their superior intellect/knowledge/whatever by insisting on minutia. Software engineers are often proud and take code reviews as a chance to flaunt their intellect, while at the same time being defensive of their opinions, and argumentative to the point of arrogance. Few senior engineers are humble, and consider code reviews as a collaborative effort to deliver the best possible solution.
In code review I absolutely hate armhair architects and backseat programmers who nitpick and theorize over every little architectural decision you made, every variable name, every loop, every comment (even complaining something is /too well commented/). They weren't the one to dive into the problem and consider it and fix it but they think their knee jerk reactions are always more valid than the remote possibility you know what you're doing as the person in the trenches. I'm going to paste a monologue I gave recently:
sometimes as people who care about our craft, we need to step back and keep the big picture in mind. the goal is to make a functioning website, or video game, or text editor. the user couldn't care less about your code formatting, composition vs inheritance decisions, or commit messages. these are useful tools to the extent that they make it easier for yourself or others in the future to provide more value to the user.
at my job, people (mostly external contractors) might make pull requests with formatting annoyances, pointless null checks, getters and setters that have no reason to exist, useless comments, "== true", things of that nature. I'd ask these to be fixed and sometimes they would be, but other times someone else would just come in and approve the PR themselves.
I realized one day that not a single one of those ugly PR's that made it through have ever come back to bite us in any way. everyone's time was better spent continuing to work on providing value to the user instead.
I hold myself to a higher standard, but when reading others' code, I've found it really means nothing to me where they put the brackets or what naming convention they use, or even if they change these up randomly. I care that it does the thing, has no glaring red flags, looks appropriately performant for the problem at hand, and won't be a maintenance nightmare (will spending 30 minutes changing something right now save at least 1 hour dealing with it later? will those code even be around by the time we get to that point, or replaced by something else? in many cases probably not, of course consider how foundational or isolated the code is.) in all my experience, any bikeshedding over aesthetics has proven to be a total waste of time.
if we're making a thing that lets the people designing a website make a thing for the visitors, I care that the experience is good for the designer and the visitor, it does what it should, etc. and it's reasonably maintainable, but I'm not going to drag someone into the bikeshed and beat them with the tire pump of subjective aesthetics until they rename their variables to my whimsies.
I have grown to learn to choose my battles when the PR is in a corporate setting. There's a lot of incentive for performative optics there, which makes people to prioritise "looking good" than "delivering good software".
If the reviewer wants me to add an unnecessary `try... catch`, I simply do that. No point arguing with someone's ego, which takes way more time. If the reviewer is asking me to code for the eventuality that Google will suddenly change their the JSON model in their API response? You got it bud. You want to preserve some convention without explaining how it applies here? I will squish my code to it, keeping it correct.
I know it makes my code less readable, more complex. But I care about delivering the value as early as possible. Also the code is not my baby, it's the company's baby. I try to fight comlpexity in the design phase, while also ensuring the ego folks get to have their optics.
It’s all about taste. Some engineers have it, some do not. And yes, taste is subjective, but the Mona Lisa is the Mona Lisa regardless of how many people don’t like it.
The most important thing about code is probably its correctness… but that doesn’t mean other less important things are not worth looking at (whether to do so when reviewing a PR is a different topic, though).
It also depends on whether one feels too attached to the code in question. If I just landed in a codebase and someone asks me to review a PR, well I guess I don’t mind too much (yet) about things besides correctness/performance/etc. But if I have been maintaining a codebase for years and suddenly some newcomer opens a PR with correct code but that doesn’t have “nice” variable names (at least in accordance to the rest of the codebase), well, I do care about that.
I say leave taste to the artists. Our job is to herd electrons through sand such that it does something useful for someone else, not produce art. We would all be better off focusing on the machine's point of view of the code instead of misapplying our mate selection circuits it.
> pointless null checks, getters and setters that have no reason to exist, useless comments, "== true", things of that nature
Aside from useless comments, if someone opens a PR with any of those things, I can only assume it’s because they don’t know why they aren’t necessary. As their colleague, I feel compelled to tell them, because personally I cringe at the thought of doing things that aren’t needed and nobody having the courtesy to tell me otherwise.
I occasionally question the value of the pre-commit code review. In general, there are two options - a quick spot check or a “deep review”.
A “deep review” involves completely understanding the code, verifying that it works, refactoring sections or even completely rewriting the original. This type of review can easily take 30 - 70% of the original effort.
A quick spot check type review involves rapidly scanning the code for code standards or anything egregiously incorrect.
There doesn’t seem to be much of a sweet spot as either the cost is too high or the value is low.
I think most reviews are closer to “spot check” type territory. Maybe this can just be done by AI and deep reviews can be done on an as needed basis.
Perhaps we can eschew the code review and simply expect that developers do high quality work. When this doesn’t happen, processes could be in place for needed fixes post commit - obviously needed anyway since most reviews just scratch the surface.
This is a bit of a strawman, but I expect most have felt that in-depth reviews require too much time while quick spot check type reviews add little value and interrupt everyone’s flow considerably.
I find this kind of backward. IMO it's more important to establlish code standards (code style, etc) up front, so I would actually place this as the base. API design is important, but if designed well (e.g. versioned), it shouldn't be hard to extend/migrate. Whereas changing code style or practices around it require a lot more work, both in terms of coding and culture.
IMO code review is way too late to be talking about API semantics unless you're at a really tiny startup. Once you get to even 100 engineers, this stuff needs to be hashed out with stakeholders before you start writing code. this can be as easy as a 10 minute conversation, but sometimes it's not so simple and doing it up front will save you tons of time.
overall this is great. but I'm surprised that meeting acceptance criteria is under "implementation" and considered less important than api design. I agree that api design is harder to change later on, but the most important question is, does the PR solve a problem that currently exists? if it does, then you can think about whether it does it well.
it makes me wonder if the product/engineering split has grown wider than it should. we really should be pretending we're product managers more.
Code review generally requires a peer to do the reviewing, so most folks would likely say that doesn’t count. I was curious if it would, but the first couple pages of googled results confirm the peer aspect.
That's certainly better than reviewing your own code, but tossing your code into a merge request to run tests and then taking another look over it tomorrow if it's all green is still better than just pushing straight to prod because there's not another pair of eyes.
That seems almost ridiculous? You commit code that you wrote and then Code Review your commit and then verify it? Doesn't that happen as part of the original commit by common sense?
But you wouldn't commit that as there are multiple tools to review a commit before you literally stamp your name on that commit? Otherwise you are generating noise for your teammates.
In practice, reviewing your own code in a different context really makes a difference, at least for me. Not sure why, but I always notice things I wouldn't before. The same effects holds true even for regular text - as much as I try, proofreading my writing in editor yields subpar results compared to reading a submitted message, I simply fail to notice even the most blatant mistakes. I wonder if this effect has a name.
My take: if you submit code without the approval of your peers, and that code causes a noticeable issue in production… well, everyone will think “if only they have added me as a reviewer we wouldn’t have this issue!”. Sure thing, the bug could still exists in prod even after peer review, but then at least that would have meant that the bug wasn’t easily discoverable (i.e., the author and the reviewers missed it).
If you think you’re smart enough to submit code without bugs and without peer review, that’s fine. Take into account that your peers may not think you are that smart if you do that. Software engineering is mostly about dealing with people (well, almost everything in this world is about that).
Obviously if there’s no one to review your code, you may as well go ahead and push it without approval (unless your company has policies against that).
To me, code review is not just there to produce better software, but also better developers.
It's a great place to talk about tradeoffs, alternative approaches, reasoning behind changes, etc.
Maybe less useful in huge projects with many contractors you're never going to work with again, but I've seen the mentoring aspect have great benefits in small teams.