Changing how I review code
itnext.io
itnext.io
disclaimer: the majority(not all) PR's were in a very very large terraform codebase.
The old me would read it, and write something like "adds S3 bucket and IAM role".
The newer me would write it as:
1. Creates S3 bucket <XYZ> in account <DEF>.
2. Creates IAM role <ABC>, with an attached policy granting read-only access to S3 bucket <XYZ>.
It seems like a small change, but when I'm actually planning to type out those details - I'm more likely to notice typos in the name/account/role/etc, or just outright errors. One of the mental shifts from my old reviews to newer ones is that I open the PR assuming I don't know what it does yet. So I have to read it for myself and interpret it, then summarize it for someone else.
Writing a summary document really forces you to get to the nitty gritty and catch things that a casual review would miss.
Our CI automatically deploys a "review app" when someone creates a pull request. This simplifies code review, QA and demoing!
For instance, in a warehouse automation infrastructure that has 30 devs, working on 8 feature simulataneously, it is next to impossible to create review app with the resources they need.
I maintain one frontend in this stack, and the review app produced will only contain my version of the front-end, and the API calls will go to the backends in the development environment.
Review apps for backend services will also point to the development environment if this particular backend needs to communicate with other services.
When a feature touches a frontend and a backend simultaneously, we can solve that in various ways. Sometimes the backend can be finished first, but if that's not feasible we can supply URL overrides for endpoints to the review app of the frontend, so it communicates with the review app of the backend (and uses the rest of the development environment for the other calls).
This works for us and might not be feasible for all projects/stacks/companies for various reasons. YMMV I guess :)
Let me put it in another way, would it have been if you would have kept, development, test, staging and production environments up to date all the time and had meaningful integration tests?
Depending on your system, this is entirely doable.
Using serverless hosting, the hosting bill is < $10/mo.
- Most style issues should be caught by automated tools
- Most functional issues should be caught by tests (written by another person preferably)
- To bring someone up to speed on your chosen style, pair programming is much faster than code reviews
- To have shared knowledge of code (increased bus factor) pair programming, or code walkthroughs are much faster
Not sure why we have fallen so much in love with the idea.
Because organisations obsess about risk minimisation.
To me code reviews can catch style and functional problems in code that get by your automation systems, but the primary impact of a code review is on the team, not the code. It imparts not just an understanding and ownership of the code, but also establishes/negotiates how the team works together.
The code review process I'm familiar with is: open a PR, wait for a couple of reviewers to comment on the work. Then normally what happens is:
- The comments are trivial, so I implement the changes (these are almost always things that could be automated, but for one reason or another haven't been).
- The comments need to be discussed/clarified, so I need to call the person.
The code by itself doesn't have all the context, all the things that were tried etc. A code walkthrough is a session with other coders, where it is explained how and why we arrived at the solution, what considerations were made and so on. Walkthrough sessions provide much faster feedback and all improvements can be discussed immediately with much greater clarity.
Sure, reviews are a time sink. No one likes them. We are still better off having done them.
Hence, a combination of intense pair programming and knowledge sharing sessions in combination with quick & lightweight PR reviews as well as regular refactorings has worked best in my experience.
- Does the code make sense to a human?
- Is this the best way to achieve the objective?
- Is the thing that the code is trying to do even something that should be done?
Some style issues can be, others (“do variables, functions, etc. have meaningful, accurate, relevant names?”) can’t be, until the automated tools are backed by AGI, but at that point “code review” and “automated analysis” become...not so different.
Nope. People have biases, and if you are teaching coding practices via pair programming you will just propagate those biases. By having a new team member receive feedback from multiple members, these biases are more likely to average out. For style specifically, it's also wise to get reviews from other teams for new people.
I do a lot of Typescript coding. Using Prettier/eslint rules, a host of (e.g. hundreds) of common stylistic and functional issues are caught in CI/CD automatically.
Better yet, I've made a `npm run fix` command that will fix 99% of them instantly.
If your tooling can automatically bring the code into compliance, then folks won't push back and will be highly likely to comply :)
You should run it somehow, but if you've got CI running tests, which I think you should have anyways, then does it matter?
In that practice, it's better to run it locally! For each UI file / function, try to get it to execute from a user's POV.
Just kidding and in reality UI tests exists and aren't super difficult to set up. The front end has plenty of tools like Cypress and its pretty simple to automate running a bunch of tests that screenshot and diff compare your site.
I never really pulled down UI code to test them out. There has to be a certain level of trust between engineers. Sometimes I'd ask or post screenshots of changes but that was rare.
In a well oiled machine, that's what the QA department does. They have close working relationships with the engineering and customer support departments so they develop testing processes and systems tailored to the "quirks" of engineers and users. They spend extra time on the places they know the engineers have blind spots and work on automated regression tests based on customer support workloads.
Whether they go as low level as unit tests depends on the organization but one place I worked at, the domain was complicated enough that "I see no red squiggles in my IDE, pinky swear" was the threshold to go from engineering to QA. It was an ongoing joke that whenever there was a major dependency update, 10-25% of the work sent to QA that day contained obvious syntax errors and wouldn't compile because the IDE everyone used often took an hour or more to reindex the project without any clear indication that it was in a useless intermediate state. Running the compiler would slow it and the indexer to a crawl, so the engineers just YOLOd it so they could move on to other work, and the QA department set up a static analyzer with an empty rule set on all PRs to catch dumb errors caused by immutable process/purchasing decisions.
IME these kinds of QA departments are the #1 springboard into software engineering roles because they work so closely with engineering and are exposed to code in the form of automated tests.
And don’t tell me your tests tell you, because I guarantee you they don’t!
It’s somewhat similar to the practice of automated code fuzzing, only manual and scoped to changes in a particular PR.
I like to actually reset the branch so I can see all the raw changes in my IDE, this helps me to think it through, in git you can do this via:
git pull feature-branch; git checkout develop; git checkout -b my_initials/feature-branch; git merge feature-branch --squash --no-commit
If you don't trust your tests, write better tests.
If you don't trust your migrations, write better tests.
Because otherwise that's how you get write-only code. Things like code style and documentation can make a big difference, and review time is when you can help enforce them and build team culture and knowledge.
There's a human instinct towards laziness. If there are no outwardly-visible reasons to distrust something, it doesn't get evaluated. It's why a hard-hat and a clipboard can get you backstage, and why you don't spend 2 minutes scanning every bush for crouching predators before you walk past it. In everyday life, a "lazy" tendency saves time by avoiding needless work. In engineering, academia, etc., it can create dangerously low standards.
I find myself asking "what is this supposed to do?" and "does this actually do what it is supposed to do?". I tend to drift into a design mindset during test writing (more-so than in code review) that highlights data flow and the basic bits of logic. Even then, I find that I can miss test cases rather easily.
then they don't need to spend all afternoon watching me get tea and pick my nose and we still get to have the interesting discussions.
What kind of PR process do you have where reviewing the PR takes as long as writing the code in the first place? Because that's the only way pair programming is as efficient as code reviewing.
If I'm doing a PR (e.g. for a new dev), and it's becoming a real bloodbath, I stop doing the PR and do a 1:1 pair programming session w/ the dev. This helps save face and keeps the advice private and inculcation.
Don't let PR's turn into public floggings.
"This code works when you run it" is the last thing I care about when I'm reading the code - it's a given, we won't allow change to merge if it wasn't the case. Code reviews are there to catch mistakes in design, copypasta, how the change fits into the product overall.
I see some people pulling down code to check for "correctness" but I don't think reviewers should be testers. I see the focus being on architectural feedback, sharing of knowledge, etc.
I can see within some specific fields pulling the code to do a UX run through but that isn't all fields of software development that that is even needed (99.9% of the time I've never felt the need) and mostly I see authors posting screenshots or screencasts to help reviewers with this.
> There is a sense that pull requests actually might make it too easy to comment on a line of code. This ease might be leading to the conflicts and hyper-zealous commenting that frustrates many in our industry.
If there are a lot of comments, I usually find its because of either a disconnect between author and reviewer that should have been addressed before authoring the code or there is something that needs to be automated.
How often is this even feasible though? sure if there's little difference between the PR branch and whatever the last build I've done is, it wont take too long to build but then I have to install our product and get it set up to test. We have a ton of legacy code that isn't easily testable. We do have a CI build but it takes like ~6 hours to build.
I much prefer to go in and check people's small commits to master than look at a huge PR before it gets merged in, but it requires you (and the whole team) to be really diligent and do it on a daily basis, and when it comes to big features, you lose a lot of the context. OTOH, the amount of times I've been presented with a huge PR of an entire feature where the author begins by saying "sorry for the huge PR" and my predictable reply "I'm having trouble getting the whole picture here, but LGTM?" are countless, so I'm not sure that's better either.
Not sure how to reconcile the two models into something great.
As for the style of code review, I ask the team and adjust accordingly. I can be "big picture guy", or I can do that and simultaneously be "Mr. Nitpick" if people so wish (some people actually do, me included).
Oh man I'm so guilty of this. If there's a spec, I basically say "YEP LOOKS GOOD!"
But shamefully, I've been bitten by this when the spec itself is written with the wrong logic. Argh!