We don’t have time for code reviews
blog.8thcolor.com
blog.8thcolor.com
Proper code review is done:
- by another programmer
- by someone with some knowledge of the application
- by someone with some knowledge of the environment
- against some code standard
- against standard requirements (APIs, database, etc.)
- with a checklist
- uniformly (no matter who is doing it)
- until it's right
Improper code review is done: - by a non-programmer
- fully automated
- as a "rubber stamp" on an approval checklist
- against the standard du jour
- according to the whims of the reviewer
- with a deadline for the next step
- as a replacement for testingAnd I can tell you: a web form capable of SQL-injections is still 'working code'. So it gets a positive 'code review'.
Thanks!
The clever idea is to make your coding standard mechanically verifiable.
I need to write a shorter version, to make the data more broadly accessible.
1. Variables declared or assigned, but never referenced again
2. Improper variable scope (local/global)
3. Reserved words (by language, framework, industry, app) used as variables
4. Improperly reused variable names
5. Improperly named variables (1 or 2 characters, contained in another variable name)
6. Too difficult to understand variable or function names
7. Function names must be Verb-Adjective-Noun
8. Variable names must be Adjective-Noun
9. Improper data type
10. Repeated (in one or more programs) code for which reusable code should be written. (Any future maintenance must only be done in one place.)
11. Fresh code rolled instead of using existing reusable code meant for that purpose.
12. Partially complete data base updates
13. Improper data base locking
14. Non-standard UI widgets
15. Version control problems (didn't start with current production version)
16. Not all changes documented with ticket#
17. Case/switch statements without fall-through clause (case 1)
18. Improperly structured if...then statements
19. if...then statements too long (according to published standard)
20. Improper indenting/spacing (according to published standard)
21. Comments don't match code
22. Comments inaccurate
23. Calling/using obsolete/deprecated code
24. Not backward compatible
25. Obviously works under only certain conditions (which will be caught in testing)
26. New code inserted in wrong place (if...then, case, function, etc.)
27. Obviously missing data base updates
28. Obviously missing API calls
29. What else?
alert("Fuck");
It made it to a single user who was testing it for me, on the other side of the planet. He spoke very little English, and for that I am grateful.That was my last push without review.
"I don't use bad words in test code."
This is actually really important. We had this happen with a customer whilst UAT testing. The project schedules were under pressure and as a result the customer went nuts (rightly so) and we had to make some concessions to customer as amends.It isn't just curse / bad words. Equally as bad are comments like:
"this was the customer's stupid idea not ours"
Also, if you really have to output test stuff in the browser use console.log('message') and not alert('message').Then for production you can always do this at the top of your main JavaScript include:
var console = {};
console.log = function(){}; window.alert = function(){};Not very helpful if someone else sees it, but also not profanity.
Then for production you can always do this at the top of your main JavaScript include:
var console = {};
console.log = function(){};Git pre-commit hooks for alert are helpful since few and far between are the circumstances where an alert is actually wanted in production. While you're at it you can add one for whatever set of profane terms, if you roll like that.
alert("Fuck"); // DEBUG FIXME(dlitz)
You can't see it here, but the comment also includes a few trailing spaces.This way:
- "FIXME" gets gets highlighted in my text editor
- the whitespace triggers a git warning when committing
- I can configure a pre-push hook that refuses to push commits to origin that contain "DEBUG FIXME"
- I ever do accidentally push it, it's clear that the code was just there for debugging, and that I'm the person to talk to about it.
I also use "git add -p" to do a hunk-by-hunk review of my changes before committing them.
None of this is very hard or time-consuming, and I can't remember the last time that I accidentally pushed debugging code since I started doing this.
1. How hard it is to grok code that I've never seen before
2. How hard it is to grok my own code from 6 months ago
Sometimes it may take me a day or more to fully get my head around a new piece of code I am working with. I could not give a thorough AND fair review of another person's code without internalizing it to a satisfactory degree.
There is also the issue of software as art vs engineering. Quite often I will take issue with the style or approach of another person's coding simply because I would have done it differently had I done it myself. But that does not mean that their code is wrong.
All things considered, I think code reviews can be helpful in certain situations, but I think there are many pitfalls which must be avoided in order for them to justify the cost.
Also think of code review as a fantastic learning opportunity for the developers you are reviewing. Practicing an art can only get you so far as an expert, but being critiqued by others can help refine the rough edges you may otherwise ignore throughout your career.
Code reviews don't have to happen in massive context, either; why not follow a practice that every pull request (or whatever your SCM calls it) goes through at least one reviewer before being merged to master? And ensure your pull requests are frequent and preferably 100 lines or less!
A little bit goes a REALLY long way. Don't give in to the fallacy that your code isn't either worth it or is good enough to not be reviewed.
Our typical PR is 1-2 days of work, so I'm reviewing several by week, something by days.
You are of course right that it should not fall into "you should do it my way". Now, when my colleague said "I would have done it differently", I always ask how and why. I will probably not change my code if it is good (or even good enough), but I would have learned something, or got another point of view.
> Sometimes it may take me a day or more to fully get
> my head around a new piece of code I am working with.
Are you trying to perform a design review, or a code review? Code reviews (in my mind, at least) concern themselves with the low-level mechanics of the code ("Handle this exception properly","there's a library method for this logic","follow the team's coding standards", etc.) whereas design reviews deal with the "how is this thing supposed to work?". When reviewing code, I avoid dealing with design issues. If I see design issues, I ask the developer for a design review.When I review code I review all of it, not just the easy parts.
If I was going to do a 1/2 ass job of it I wouldn't do it in the first place.
> When I review code I review all of it...
We haven't mentioned timing, but that's the reason I decouple design and code reviews.Design reviews can be done fairly early in the process before the code is complete. If design changes are needed, there is still plenty of time for them.
Code reviews can't really be done until most of the code is written, but (as you point out) the kinds of problems they find are much easier to fix, so require less time.
1. Reduces bus factor.
2. Increase readability.
Coding in a company is a communication problem. How you solve a problem may be different than someone else. Outside of simply catching more mistakes, it's possible that you may learn something new or the other person can improve their code.
Ideally there should be a team / company coding style simply to ease communication overhead and reduce bikeshedding.
> We started doing Unit Testing at some point, and it
> quickly became “good practice/mandatory” in our team (I
> think good practices need to be applied by everyone in a
> team, requiring some kind of “collective enforcement”).
I'd love it if the author would elaborate more on how to change team behavior like this, especially when it's a team member (peer) trying to make the change.I've seen many teams struggle because there isn't a agreement over what practices to follow.
[1] See edw's comment on proper code reivews: https://news.ycombinator.com/item?id=6598804
1. As an advocate for a practice, ensure you use that practice in all that you do. Advocate for it in every team meeting. Try to stress why it is important, and how it can directly make things better, instead of just "making our code better." Provide research and numbers from prior projects that show how useful such a practice can be.
2. Provide good examples and guides to get the practice started. Some avoid a practice because they don't know the best way about executing a practice. If it's unit testing, show how one should go about determining edge cases and describe useful unit testing methodologies (mocks, stubs, etc).
3. Make it easy as possible. If it's unit testing, integrate a CI server directly to your SCM. If it's code review, adopt a practice that fits inline with your existing workflow and don't make it a ceremony - make it asynchronous.
4. Understand that it will be gradual, but every effort moving forward should push towards integrating the new practice. You can't suddenly have 100% test coverage after introducing unit testing to an existing project, but all code that gets included can include new tests, and old refactoring can have tests included to slowly build up the test coverage.
As a team leader facing junior developers, I did simply set-up rules. I explained them, but I had the power to enforce them by myself. I coded with them, and they started coding like me - until they were confident enough to challenge me. I apologize to the "autonomous self organizing teams" evangelists, but in some situations, giving some direction may be the most efficient way to progress.
As a peer, you just need to find one other person in your team that is willing to play along. Although I understand the value in explaining something, doing it is for me much more convincing. If you really think something is a good practice, don't try convincing me if you are not already applying it yourself.
If you are in an Agile/sprint oriented team, just propose to test it for one sprint, then it will pass the retrospective test or it will not - no one should object to a one sprint experience, especially in an agile team. Do the same with your colleagues ideas, even if you find them silly. It shows goodwill, and you can be surprised at some time.
This is actually the way me arrived to our current workflow at 8th color (http://blog.8thcolor.com/2013/09/how-our-own-workflow-is-dri...) - successive retrospectives.
Finally I would not involve non coding management in the discussion if possible, as it will quickly devolve into "what would it cost". Better to handle this inside the technical team.
Hope it helps, and remember, it's the first step that cost. Find one willing colleague and start!
Martin
There's also the added benefit that reviewing your code can teach them about better programming practices, and also that reviewing their code with them and asking questions about their thought patterns can make them better developers.
It's my opinion that if the code is awful then code reviews become especially helpful and important to bringing everyone up.
If it's not something you can stick on an invoice, it's generally hard to persuade management it's worth doing in my experience.
The fall in the technical/developer responsibility anyway for me.
The developers should write new test code for every new feature and run all the existing test code after/merge before checkin. All the projects I worked on that implement this process are very successful. With this process in place and agree upon, I careless about code review.
With enough "functional" test coverage, it is easy to do massive refactoring of code without worry about any breaking.
Worked on one project that try code review for two weeks - other than a few comments about coding style, not much gain from the time spent.
Besides that, it sounds like a nice codebase to work in. I've had very good test coverage for libraries and backend services with well-defined APIs I've written, but a lot of my career has been spent doing UI work and I've never found an adequate testing tool that can deal with both the complexity and randomness of human interaction and the rate of change of UI presentation.
You can't really regression test UIs, because most UI development intentionally changes the things regression tests look for, so you wind up spending a lot of time updating the regression tests and trying to keep up with UI changes that are being made in a tight code/review/tweak iteration loop.
A major part of being an effective programmer is the ability to maintain state and call stacks in your head.
Stepping through code is great - as a last resort. But I've found that if I need to run through it with a debugger in order to understand it, it is often overly complex.
As with any generalized rule there are exceptions, but I consider needing to step through code in a debugger to understand its behavior as that exception, not the inverse.
They are always attached to a branch though so you can check it out to review (and run/test as appropriate).
Martin (OP)
Static analysis and regression tests are tools to make sure the code isn't broken.
Code reviews are also about distance from the code. When it's your production it's easy to miss the forest for the trees. That's why books are generally better when beta readers or reviewers are involved.
Code reviews are also about spreading knowledge (about the subsystems and about choices made in implementation and the reason for them) and increasing the code's bus factor.
CI may run the tests, but that assumes your tests have captured every possible edge case. It may be the intention of tests to be comprehensive and cover everything under the sun, but that is rarely the case in reality.
Ditto QA - they should be able to black-box test the software and all of its possible states, but in reality something is going to pass through the net.
Having a dev run the code themselves and poke around in it is just a plain good idea.
Why? If they are reviewing the code including the unit tests, shouldn't they instead be suggesting any missing unit tests, which then become permanent and reusable rather than "doing some manual testing"?
> CI may run the tests, but that assumes your tests have captured every possible edge case. It may be the intention of tests to be comprehensive and cover everything under the sun, but that is rarely the case in reality.
Insofar as the other dev doing code review can address this with their own testing, isn't it better for this to be done-once and preserved by adding automated tests rather than done-and-lost by doing manual tests?
We have tests in place to help stop regressions, but sometimes seeing a change in action can really help to put the corresponding code in context.
Most of my team uses hub, but you can check out a pull request easily enough with git:
git fetch <remote> +refs/pull/<pull-request-number>/head
git checkout FETCH_HEAD