Code reviews need to shift-left
sourcery-ai.medium.com
sourcery-ai.medium.com
If your coworkers are sending changes for review that don't explain why the changes are being made (and therefore provide the context), reject it until they write a proper summary/test plan that does explain it. You should review the "metadata" of changes before you ever review the code itself and if done well, you'll probably be able to guess what a lot of the incoming changes are before reviewing the code. This makes it easier to spot code that deviates from the stated purpose of the changes and can help identify faults in both understanding of the overall problem or the system being built, especially in more junior engineers.
If your coworkers are taking to long to review changes, work with your team to incentivize expedient code reviews, or talk to the people that aren't reviewing enough code to help everyone else get more done. You should also utilize a stack that lets you continue progressing with your work even when waiting for reviews (like stacked diffs).
That's a great approach. It sounds logical, but I certainly don't do it consistently. :-)
If you think about a pull request as a story you have who, what, where, when, and why. Most tools handle the first 4 but the why is critically important to anyone, including yourself, who needs to understand the nuance of the change that can’t easily be expressed by the code itself without a novel size comment block.
I find that a lot of times people forget the “why” when submitting a change set and simply restate the “what”. At the time you make the change it’s all fresh in your mind and you trick yourself into believing you’ll remember all the details but in reality that’s no my experience.
I do have the longest code review summaries on my team, amusingly. So, I clearly like the idea of it being a discussion and capturing some of that. I would be challenged to say what part is most important overall, though. In large, because not all code changes are the same.
That's a great question. I guess it depends on how much the code will change in the future. These things CAN get relevant again when you consider changing that code. E.g. if it turns out later that the library I've chosen isn't maintained anymore. Or we would need some more functionality, but this library doesn't provide it. In these situations, it's useful to see which other alternatives we've considered.
That said: I can't really estimate how often this turns out to be relevant, indeed. I guess the answer might be very different for different projects.
Everything you say that's not written down will be lost in the aether within weeks, months at best. I'm a huge fan of async communication because everything will be written down.
If there's a discussion "why did you go with A and not B", in an (async) code review then anyone can come back to it a year later and read up on why it was done in this particular way. I've found this useful on a number of occasions.
It certainly beats "Alice, you changed this last year, but why is it done like that? Wouldn't X be much more convenient because Y? It's hard to add Z now.", "ehm, I discussed it with Bob, but he since left the company. I don't quite remember, I think it was A? Or maybe B? Or was it C?"
But still we do these after-the-fact code reviews because I don't want to bother my colleague N times in the process. So I churn away until I'm done. And then not to bother them twice, I also polish it. And THEN I ask for a review. The reviewer sees something polished and finished, and assumes that a) since it's polished I must have spent a lot of thought around whether it's the right thing, and obviously anything they can do in the review time which is never going to exceed 2% of the dev time, is rarely going to result in them second guessing that and b) since it's polished they know they'll probably step on some toes if they say "throw it out and do something completely different". They can see the effort. The relationship with a colleague is more important than even the health of the project. So they approve it.
The problem of course then is: how do you get from the "too late code reviews that they are rubber stamps" into a process of dialog, half-way reviews and iteration? Other than pair/mob programming I don't know. We say we don't want more processes, but whenever there isn't a process for this iterative behavior, the result is people developing for too long on their own and producing results that are too far gone to be shot down.
It’s best, I find, to get that kind of design-level feedback early and review it when needed as progress is made. No sense waiting until you’ve polished it to hear that you wasted your time building the wrong thing.
Another strategy to cope: small changes merged early and often. This requires a bit more work on the part of the author to keep the continuity of the plan moving in the right direction over several PRs. However, it has been demonstrated empirically that humans can’t review much more than a couple hundred lines of code per hour [0].
And lastly, as a reviewer, demand evidence as to why the change is correct. Don’t bother yourself with reading every line like you’re some compiler that’s better than your compiler. Instead validate the reasoning of the author and read the tests/evidence/proof. You need a fairly experienced team that are sophisticated enough to write good specifications, but even good unit tests go a long way. This way you only have to check the author’s argument.
If you’re using languages with manual memory management make sure you’re using the right testing strategy that will catch errors in your program. Write unit tests at the bare minimum. Property tests would be preferred. Fuzz tests, integration tests, etc on top. Whatever it takes: catching errors in programs is surprisingly difficult for humans regardless of experience or training. The JVM had a critical error in its binary search implementation that hid for nearly a decade, OpenSSL, etc.
Code review is not about reading every line and playing, “spot the error.” You’ll miss some. Your team mates will miss some. You need to think above the code to catch those. Time is precious and life is short. Spend review time effectively by making sure you understand the specifications and that your colleagues have done their homework.
Happy reviewing.
[0] https://www.researchgate.net/profile/Ahmed-E-Hassan-2/public...
Think of it this way: the maintenance burden of your (lower-quality) work if you don't bother your colleagues throughout the process will be way more bothersome than checking in up front.
At the very end, they are of limited value and seen as a chore. So they become rubber stamps. The quality of the code base suffers from this, making development slower.
Everyone knows what the end goal is, yet "Code review finished code" is easy to adopt, but not very efficient unless the org is extremely good at what they are doing.
"Pause for thought and collaboration half way" is much much harder to achieve, without taking it to the extreme (pair sessions enforced etc).
I have no idea why people think review of final code should be the default, as you say it is rarely useful.
Plus besides problematic architecture there are a lot of bugs that are easy to fix but that that one does not when developing the code. It is much easier to discover those in polished code than in the first working prototype.
What, why?
Hum... You mean you don't want more meetings? You can't have more or less processes, their amount is completely defined by the diversity of your work.
If it's about meetings, yes, synchronicity is always a problem. But you have synchronicity on the review already, it can't be too bad to just move it into another time.
Years of working together in a team on the same thing to gain trust.
I have found that pair programming destroys the ability to coherently think about a large portion of work for a large portion of developers. It is not easy to be "on" all the time, and a lot of people work at a pace or in a structure that is not compatible with that.
There are many arguments for both sides.
Pro pair / mob programming:
* Faster feedback. * Less time spent on solutions that might be rejected. * Better knowledge sharing (for those who participate).
Pro async review:
* A more neutral, "external" view. (Smaller chance of groupthink.) * More empathy with future readers of the code. * Encourages to make assumptions and decisions explicit. => Better knowledge sharing for those, who weren't involved in the writing of the code.
I hope I never have to use git flow or alike again in the future!
Personally, I like the process. It allows us to move quickly, and focus on blocking changes. We can still get reviews prior to pushing code if it is sensible (for large changes), but most (80%?) changes tend to be quite small.
What qualifies as a "small change"? Do you have some numbers to measure it? Or is it the developer's call?
However, our timezones only spanned three hours.
- all within one time zone
- all on senior level
- not an answer but just for context: we didn’t had any prod incident within 3 years
- we work fully remote
- we do mob sessions most of the day
- developing the e2e tests for the modules we wrote took not that much of time. Maybe 2-3 weeks for each product/module . Most of th me times it’s out something on s3 Herr and check API or DB there… most modules are trivial data transformations tbh but in the end most software i ever touched was…
Can you tell a bit more about those mob sessions? Do all 4 devs participate? Do you follow some structure?
How often do the non-coding participants comment on sth? Is it more like a continuous conversation? Or rather one person coding and the others occasionally chiming in? How long is such a mob session?
I'm also wondering what kind of team setup makes such a model feasible and successful... By the way, congrats :-) 3 years without a prod incident sounds impressing, indeed.
How long has the team been working together? You've mentioned that all devs are on senior level. Do you have some kind of hierarchy within the team?
Hm, the team is working together for maybe 2 years like this. Those sessions go the whole day with a few breaks (lunch, coffee, bathroom).
No hierarchy and we are all involved during the sessions but if you have a bad day it’s ok to be more passive…
We were told that we are quiet performant in comparison to other teams but that is maybe also due to the fact that we are all seniors and trying to be pragmatic with our decisions
Flip side of this, you can tackle both of their issues with better tooling and not throw the baby out with the bathwater.
Time lag: Stacking (https://graphite.dev/stacking) is a powerful technique that allows developers to keep developing even while others read through their code.
Context gap: you encounter the same issue in an IDE when exploring a new part of a codebase, and there are tools to help you there (click to see function def, open relevant tests, AI summarize what its doing - we've been trialing the last one internally and it's so cool). Your CR tool should offer all of that, and at many large companies (Google, FB, MSFT, even Jane street) it does. GitHub is just deficient here.
I agree with the other comments in this thread that the main problem is that what should have been an architecture / prototype / etc review happens in CR, and I agree with that. But I think moving away from CR worsens that problem not improves on it.
Yes, that's one of my major arguments for async reviews, especially in large organizations: Future readers of the code will have ca. the same context as the async reviewer. (Contrary to a pair programmer, who has much more context.)
"there are tools to help you there" That sounds very interesting. Can you elaborate on that? Which tools do you use when getting familiar with a new part of a codebase?
Function def and relevant tests are very useful indeed. AI summary what it's doing. That sounds intriguing. Which tools do you use for that?
How do you get the broader context? E.g. how do you decide which other parts of the codebase are closely related (and should be included in your exploration)?
Shifting code reviews left seems to be either (or both of):
- look, I made a poopie!
- let's do Big Planning Up Front (agile has failed)
Yes, it does little bit to spread knowledge - but it is not much effective at that. Actual occasional architecture session where ideas are explained is much better. Yes, it does little bit to catch bugs, but having actual testing in place does much more. Yes, it does something to teach junior, but for Christ sake, currently it leads to "give them task with no guidance, let them figure it out and then tell them about everything they have done wrong". That is just about the worst way of teaching people.
And it creates social issues we don't want to acknowledge, because code review is sacred and cant be criticized. Which leads to kindergarten level of advice to people who run into issues like "dont take it personaly, be nice". Which is next to useless if you have an actual social issue going on in your team.
That's a very good way to put it. Code review is useful, but the expectation around it is often over reasonable.
I agree on your points regarding quality and knowledge sharing: Code review is beneficial for both, but definitely not sufficient as the only or main tool.
"give them task with no guidance, let them figure it out and then tell them about everything they have done wrong" It sounds blatant. But often not far from what's happening. :-)
"And it creates social issues we don't want to acknowledge" Yes, that's another longer topic. :-)
Added benefits are proper top level documentation, and a log of the discussions and compromises made to cut short on future design discussions.
Edit: typos on mobile
Life is about compromise so this doesn't have to be true 100% of the time, but this should be in your team's list of core competencies.
Also RFC doesn't HAVE to mean big pedantically formatted and worded monospace document. Templates help make sure common mistakes aren't repeated, but using normal-person English and easy to understand code examples can be very effective.
This is not a very old "tradition." We started code reviews back in the 2000s with in-person small group code reviews, and only later moved to the tool-based async code reviews that are ubiquitous today. We adopted good ideas from a web article "Effective Code Reviews Without the Pain" ( https://www.developer.com/guides/effective-code-reviews-with... ). We had folks enter comments in advance but went through them as a group, using social customs like starting questions with "did you consider..." to keep it cordial and non-confrontational. A lot of these reviews generated good discussions about code quality, clarity, etc. We didn't review 100% of code using this process, and we didn't always require the review to be completed before merging - the main goal was more to improve all developers' habits than to improve one particular chunk of code.
Moving to the current tool-based async code reviews has pros and cons, but now it takes effort to ensure they stay positive, and the async process eliminated some of the great team discussions that the old-style reviews had.
Perhaps the ideal solution is a mix of both approaches?
When I joined my last team they had a bad code review process. Basically once a day or so the CTO and one or two other senior people would look at all the ready-to-merge PRs and give them a thumbs up or thumbs down. I pushed for a much more rigorous but inclusive process. Everyone would do code reviews, every PR would have two approvers, we'd talk about how to do review well and we'd invest in some more automations to solve common pain points. Not only did we get faster as a team (contrary to popular belief, more reviews != slower) but we also got much more thorough feedback AND we were able to consciously propagate the right patterns throughout our codebase in an organic way.
It's no exaggeration to say that good code review turned leveled the team more than any other engineering process change we made. More than testing or sprint improvements, more than our management changes, etc. It's highly underrated!
I can't help thinking that if you're waiting until code review to determine if someone on the team wrote the code in a way that will pass a review or not then you have some serious comms issues on your team. If this is a problem then you should be doing mini reviews during the dev process as well as a code review at the end.
Code review is a health check from your teammates who don't have the authorship bias. It is not a replacement for testing, both manual and automated. Testing is solely the responsibility of the author, code review is just a design lint check and/or knowledge sharing opportunity. You can still have a QA team of course, but that is usually decoupled from application development and is done post-merge anyway.
Every shop is different so YMMV, this is just a personal take.
Which I would addressed first by tooling of course like linters and automated code checks so low hanging fruit is out of the way.
Second thing of shifting left is what I call “developers alignment” - which is yes a meeting where people regularly discuss! things instead of coming up with stuff ad-hoc during review or expecting others to get the context from thin air. Then repeating “obvious” rules regularly because people forget and not expecting people to remember things by heart.