Effective Code Review
What to Say & How to Say It
Lone Star PHP 2017 | #lsp17 | https://joind.in/talk/2bf9b
@jessicamauerhan
Senior Software Engineer
TDD/BDD Expert
jessicamauerhan@gmail.com
jmauerhan.wordpress.com
Why Review?
● Catch / Prevent Bugs
● Identify Potential Problems
(not "bugs" but performance,
security, etc)
● Improve Maintainability of
Code
● Improve Communication and
Understanding
Code Review: Careful examination of
code, looking for mistakes and
attempting to improve overall quality.
Types of Code
Review
● Formal
○ Multiple Days
○ Multiple Reviewers
● Informal
○ Faster
○ Not always documented
○ Just as effective!
Pull Request Reviews
● Useful for impacting the writer's skills and habits
● Cross-Team Code Review: maintainable code
● Small Frequent PRs:
○ Clear, Readable Code enables reader to understand context & purpose quickly
○ Empower everyone to review code, so everyone can contribute to review
● Don't rely on one/few reviewers
Sprint Retrospective Reviews
● Useful for reviewing & communicating out to team
○ Purpose of code / feature
○ Explain new libraries / dependencies
● Extra eyes to catch any remaining flaws
● Demonstrate / enforce good habits
Before Code Review
Happens...
Attention Coders!
● Remember:
○ You are not your code
○ The Bugs are the enemy, not QA
/ Reviewers
● Your Responsibilities:
○ Document your code
○ Review your code
Automate,
Don't Debate!
● Style / Formatting / Linter
● Unit Tests
○ Coverage Calculation
○ Mutation Testing
● Behavior Tests
● Other Automated QA:
○ github:
exakat/php-static-analysis-tools
○ PHP Mess Detector
■ number of arguments, LOC,
else statements, etc
○ Copy Paste Detector
Let's Get Our Review
On!
Quick Tips
● Avoid Context Switching
● Review < 200 LOC
○ No faster than 300 LOC / hour
○ No more than 60 minutes
The Code Review Checklist
● Does it work?
● Are tests effective?
● Are edge cases covered?
● Do I understand?
● Do comments explain "Why"?
● Duplicated concepts
● Design patterns
● Maintainable / modular code
● Incomplete / TODO / commented-out
code
● Error Messages / Exceptions
● Security concerns
● Performance issues
Phrasing!
Provide Positive Feedback
● Applies newly introduced or difficult concepts
● Easily understood code, well named classes, uses third party library
appropriately, etc.
● Uses design patterns appropriately
Observe, Don't Interpret
Interpretations are what you think is
happening
● "You're trying too hard to be clever and
save lines of code here, just write it out!"
Observations are things you see
● "I think this might be easier to read if it
was separated out into multiple
statements with assignments, like this:"
Interpretation Observation
Review the Code, Not the Coder - Don't Say "You"
● "You used the wrong logical comparison
here"
● "You should use a factory!"
● "Does the logic of this feature mean this
should be a triple equal (===) instead of
the double (==)?"
● "Could we use a factory here to help
prevent coupling?"
"You" Statement "Code" Statement
Suggest, Don't Demand
● "This loop is repeated in this other
method too, and so it needs to be
refactored into a separate method"
● "Could we refactor this section here into
a separate method? It would reduce
duplication in the method above as well"
Demand Suggestion
There is no "Just Do It"
Implies it's easy, obvious, quick
● "Why didn't you just use *Library X*?"
Suggest solution and explain the benefit of it
● "Could we use *Library X* here instead
of rolling our own? It could save us a lot
of time in the future, and it's well tested"
"Just" Just don't "Just"
Avoid Absolutes
● "Never run queries in loops!" ● "Our best practices guide says queries in
loops are bad for performance, is there
a better way we can do this?"
Never, Always, Must… Ask for Explanation
Discuss Large Concepts in Person
● Complex
● Controversial
● Confusing
Be On The Same Team!
● "Can you rewrite this to use a Singleton?' ● "Could we rewrite this to use a
Singleton?"
Against Together
Phrasing!
Effective Feedback
● Provide positive feedback
● Observe, don't interpret
● Review the code, not the
coder
● Suggest, don't demand
● Don't "just do it"
● Avoid absolutes
● Discuss large / complicated
concepts in person
● Remember to be on the same
team!
Now What?
After A Review
● Confirm bugs are fixed /
changes are made
● As Needed: Discuss in
person, then document
Effective Code Review in One Slide
● Automate All The Things!
● Self-Review
● Review often, slowly and carefully, for
shorter periods
● Empower everyone to review
● Follow a review checklist
○ Logic, business benefits documented,
readability & maintainability, security,
performance and TESTS!
● When sharing your feedback:
○ Empathetic
○ Polite
○ Suggestions
○ Teamwork
● Follow up
Practice...
I have written some
code, committed, and
am about to push this
commit...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class SimpleJsonApiValidator implements JsonValidator
{
public function validate(string $json):bool
{
$object = json_decode($json);
if($object === null){
throw new InvalidJsonException();
}
if (property_exists($object, 'data') = false)
{
throw new InvalidJsonApiException('Missing data property');
}
}
}
You have been asked
to review these
commits...
<?php
namespace Src;
use Mandrill;
Class App
{
private $from = 'app@example.com';
private $subject = 'Welcome to the App!';
private $message = 'This is a welcome email!';
private $apiKey = 'C0wG3h1A5Fs5xNoLdM2S0w';
private $mailer;
public function __construct()
{
$this->mailer = new Mandrill($this->apiKey);
}
public function email($to)
{
/** Build an email array */
$email = [
'to' => [['email' => $to]],
'from_email' => $this->from,
'subject' => $this->subject,
];
$this->mailer->messages->send($email);
return true;
}
}
● Does it work?
● Are tests effective?
● Are edge cases covered?
● Do I understand?
● Do comments explain "Why"?
● Duplicated concepts
● Design patterns
● Maintainable / modular code
● Incomplete / TODO / commented-out code
● Error Messages / Exceptions
● Security concerns
● Performance issues
<?php
class SimpleJsonApiValidator implements JsonValidator
{
/**
* @param string $json
* @return bool
*
* @throws InvalidJsonException
*/
public function validate(string $json):bool
{
$object = json_decode($json);
if ($object === null) {
throw new Exception();
}
if (property_exists($object, 'data') === false) {
throw new InvalidJsonException();
}
$data = $object->data;
if (property_exists($data, 'type') === false) {
throw new InvalidJsonApiException();
}
if (property_exists($data, 'id') === false) {
throw new InvalidJsonApiException();
}
if (property_exists($data, 'attributes') === false) {
throw new InvalidJsonApiException();
}
//if( property_exists($data, 'error') === false) {
// throw new InvalidJsonApiException();
//}
return true;
}
}
<?php
class SimpleJsonApiValidatorTest extends TestCase
{
public function testValidateThrowsExceptionWhenJsonInvalid()
{
$json = '{"data":}';
$this->expectException(InvalidJsonException::class);
$validator = new SimpleJsonApiValidator();
$validator->validate($json);
}
public function testValidateReturnsTrueWhenJsonValid()
{
$data = (object)[
'type' => 'chirp',
'id' => 'uuid',
'attributes' => (object)[
'chirpText' => 'Test'
]
];
$payload = (object)['data' => $data];
$json = json_encode($payload);
$validator = new SimpleJsonApiValidator();
self::assertTrue($validator->validate($json));
}
}
<?php
class SimpleJsonApiValidator implements JsonValidator
{
/**
* @param string $json
* @return bool
*
* @throws InvalidJsonException
*/
public function validate(string $json):bool
{
$object = json_decode($json);
if ($object === null) {
throw new Exception();
}
if (property_exists($object, 'data') === false) {
throw new InvalidJsonException('Missing data property');
}
$data = $object->data;
if (property_exists($data, 'type') === false) {
throw new InvalidJsonApiException('Missing data->type property');
}
if (property_exists($data, 'id') === false) {
throw new InvalidJsonApiException('Missing data->id property');
}
if (property_exists($data, 'attributes') === false) {
throw new InvalidJsonApiException('Missing data->attributes property');
}
return true;
}
}
<?php
class SimpleJsonApiValidatorTest extends TestCase
{
public function testValidateThrowsExceptionWhenJsonInvalid()
{
$json = '{"data":}';
$this->expectException(InvalidJsonException::class);
$validator = new SimpleJsonApiValidator();
$validator->validate($json);
}
public function invalidJsonProvider()
{
return [
'missingData' => ['{"someJson":"myText"}'],
'missingType' => ['{"data":{}}']
];
}
/**
* @dataProvider invalidJsonProvider
*/
public function testValidateThrowsExceptionWhenSchemaInvalid(string $json)
{
$this->expectException(InvalidJsonApiException::class);
$validator = new SimpleJsonApiValidator();
$validator->validate($json);
}
public function testValidateReturnsTrueWhenJsonValid()
{
$data = (object)[
'type' => 'chirp',
'id' => 'uuid',
'attributes' => (object)[
'chirpText' => 'Test'
]
];
$payload = (object)['data' => $data];
$json = json_encode($payload);
$validator = new SimpleJsonApiValidator();
self::assertTrue($validator->validate($json));
}
}
@jessicamauerhan
Thank You!
Effective Code Review
Lone Star PHP 2017
https://joind.in/talk/2bf9b
Feedback & Questions?
Welcome & Encouraged!
@jessicamauerhan
jessicamauerhan@gmail.com
jmauerhan.wordpress.com

Effective code review

  • 1.
    Effective Code Review Whatto Say & How to Say It Lone Star PHP 2017 | #lsp17 | https://joind.in/talk/2bf9b
  • 2.
    @jessicamauerhan Senior Software Engineer TDD/BDDExpert jessicamauerhan@gmail.com jmauerhan.wordpress.com
  • 3.
    Why Review? ● Catch/ Prevent Bugs ● Identify Potential Problems (not "bugs" but performance, security, etc) ● Improve Maintainability of Code ● Improve Communication and Understanding Code Review: Careful examination of code, looking for mistakes and attempting to improve overall quality.
  • 4.
    Types of Code Review ●Formal ○ Multiple Days ○ Multiple Reviewers ● Informal ○ Faster ○ Not always documented ○ Just as effective!
  • 5.
    Pull Request Reviews ●Useful for impacting the writer's skills and habits ● Cross-Team Code Review: maintainable code ● Small Frequent PRs: ○ Clear, Readable Code enables reader to understand context & purpose quickly ○ Empower everyone to review code, so everyone can contribute to review ● Don't rely on one/few reviewers
  • 6.
    Sprint Retrospective Reviews ●Useful for reviewing & communicating out to team ○ Purpose of code / feature ○ Explain new libraries / dependencies ● Extra eyes to catch any remaining flaws ● Demonstrate / enforce good habits
  • 7.
  • 8.
    Attention Coders! ● Remember: ○You are not your code ○ The Bugs are the enemy, not QA / Reviewers ● Your Responsibilities: ○ Document your code ○ Review your code
  • 9.
    Automate, Don't Debate! ● Style/ Formatting / Linter ● Unit Tests ○ Coverage Calculation ○ Mutation Testing ● Behavior Tests ● Other Automated QA: ○ github: exakat/php-static-analysis-tools ○ PHP Mess Detector ■ number of arguments, LOC, else statements, etc ○ Copy Paste Detector
  • 10.
    Let's Get OurReview On!
  • 11.
    Quick Tips ● AvoidContext Switching ● Review < 200 LOC ○ No faster than 300 LOC / hour ○ No more than 60 minutes
  • 12.
    The Code ReviewChecklist ● Does it work? ● Are tests effective? ● Are edge cases covered? ● Do I understand? ● Do comments explain "Why"? ● Duplicated concepts ● Design patterns ● Maintainable / modular code ● Incomplete / TODO / commented-out code ● Error Messages / Exceptions ● Security concerns ● Performance issues
  • 13.
  • 14.
    Provide Positive Feedback ●Applies newly introduced or difficult concepts ● Easily understood code, well named classes, uses third party library appropriately, etc. ● Uses design patterns appropriately
  • 15.
    Observe, Don't Interpret Interpretationsare what you think is happening ● "You're trying too hard to be clever and save lines of code here, just write it out!" Observations are things you see ● "I think this might be easier to read if it was separated out into multiple statements with assignments, like this:" Interpretation Observation
  • 16.
    Review the Code,Not the Coder - Don't Say "You" ● "You used the wrong logical comparison here" ● "You should use a factory!" ● "Does the logic of this feature mean this should be a triple equal (===) instead of the double (==)?" ● "Could we use a factory here to help prevent coupling?" "You" Statement "Code" Statement
  • 17.
    Suggest, Don't Demand ●"This loop is repeated in this other method too, and so it needs to be refactored into a separate method" ● "Could we refactor this section here into a separate method? It would reduce duplication in the method above as well" Demand Suggestion
  • 18.
    There is no"Just Do It" Implies it's easy, obvious, quick ● "Why didn't you just use *Library X*?" Suggest solution and explain the benefit of it ● "Could we use *Library X* here instead of rolling our own? It could save us a lot of time in the future, and it's well tested" "Just" Just don't "Just"
  • 19.
    Avoid Absolutes ● "Neverrun queries in loops!" ● "Our best practices guide says queries in loops are bad for performance, is there a better way we can do this?" Never, Always, Must… Ask for Explanation
  • 20.
    Discuss Large Conceptsin Person ● Complex ● Controversial ● Confusing
  • 21.
    Be On TheSame Team! ● "Can you rewrite this to use a Singleton?' ● "Could we rewrite this to use a Singleton?" Against Together
  • 22.
    Phrasing! Effective Feedback ● Providepositive feedback ● Observe, don't interpret ● Review the code, not the coder ● Suggest, don't demand ● Don't "just do it" ● Avoid absolutes ● Discuss large / complicated concepts in person ● Remember to be on the same team!
  • 23.
  • 24.
    After A Review ●Confirm bugs are fixed / changes are made ● As Needed: Discuss in person, then document
  • 25.
    Effective Code Reviewin One Slide ● Automate All The Things! ● Self-Review ● Review often, slowly and carefully, for shorter periods ● Empower everyone to review ● Follow a review checklist ○ Logic, business benefits documented, readability & maintainability, security, performance and TESTS! ● When sharing your feedback: ○ Empathetic ○ Polite ○ Suggestions ○ Teamwork ● Follow up
  • 26.
  • 27.
    I have writtensome code, committed, and am about to push this commit...
  • 28.
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class SimpleJsonApiValidator implementsJsonValidator { public function validate(string $json):bool { $object = json_decode($json); if($object === null){ throw new InvalidJsonException(); } if (property_exists($object, 'data') = false) { throw new InvalidJsonApiException('Missing data property'); } } }
  • 29.
    You have beenasked to review these commits...
  • 30.
    <?php namespace Src; use Mandrill; ClassApp { private $from = 'app@example.com'; private $subject = 'Welcome to the App!'; private $message = 'This is a welcome email!'; private $apiKey = 'C0wG3h1A5Fs5xNoLdM2S0w'; private $mailer; public function __construct() { $this->mailer = new Mandrill($this->apiKey); } public function email($to) { /** Build an email array */ $email = [ 'to' => [['email' => $to]], 'from_email' => $this->from, 'subject' => $this->subject, ]; $this->mailer->messages->send($email); return true; } } ● Does it work? ● Are tests effective? ● Are edge cases covered? ● Do I understand? ● Do comments explain "Why"? ● Duplicated concepts ● Design patterns ● Maintainable / modular code ● Incomplete / TODO / commented-out code ● Error Messages / Exceptions ● Security concerns ● Performance issues
  • 31.
    <?php class SimpleJsonApiValidator implementsJsonValidator { /** * @param string $json * @return bool * * @throws InvalidJsonException */ public function validate(string $json):bool { $object = json_decode($json); if ($object === null) { throw new Exception(); } if (property_exists($object, 'data') === false) { throw new InvalidJsonException(); } $data = $object->data; if (property_exists($data, 'type') === false) { throw new InvalidJsonApiException(); } if (property_exists($data, 'id') === false) { throw new InvalidJsonApiException(); } if (property_exists($data, 'attributes') === false) { throw new InvalidJsonApiException(); } //if( property_exists($data, 'error') === false) { // throw new InvalidJsonApiException(); //} return true; } } <?php class SimpleJsonApiValidatorTest extends TestCase { public function testValidateThrowsExceptionWhenJsonInvalid() { $json = '{"data":}'; $this->expectException(InvalidJsonException::class); $validator = new SimpleJsonApiValidator(); $validator->validate($json); } public function testValidateReturnsTrueWhenJsonValid() { $data = (object)[ 'type' => 'chirp', 'id' => 'uuid', 'attributes' => (object)[ 'chirpText' => 'Test' ] ]; $payload = (object)['data' => $data]; $json = json_encode($payload); $validator = new SimpleJsonApiValidator(); self::assertTrue($validator->validate($json)); } }
  • 32.
    <?php class SimpleJsonApiValidator implementsJsonValidator { /** * @param string $json * @return bool * * @throws InvalidJsonException */ public function validate(string $json):bool { $object = json_decode($json); if ($object === null) { throw new Exception(); } if (property_exists($object, 'data') === false) { throw new InvalidJsonException('Missing data property'); } $data = $object->data; if (property_exists($data, 'type') === false) { throw new InvalidJsonApiException('Missing data->type property'); } if (property_exists($data, 'id') === false) { throw new InvalidJsonApiException('Missing data->id property'); } if (property_exists($data, 'attributes') === false) { throw new InvalidJsonApiException('Missing data->attributes property'); } return true; } } <?php class SimpleJsonApiValidatorTest extends TestCase { public function testValidateThrowsExceptionWhenJsonInvalid() { $json = '{"data":}'; $this->expectException(InvalidJsonException::class); $validator = new SimpleJsonApiValidator(); $validator->validate($json); } public function invalidJsonProvider() { return [ 'missingData' => ['{"someJson":"myText"}'], 'missingType' => ['{"data":{}}'] ]; } /** * @dataProvider invalidJsonProvider */ public function testValidateThrowsExceptionWhenSchemaInvalid(string $json) { $this->expectException(InvalidJsonApiException::class); $validator = new SimpleJsonApiValidator(); $validator->validate($json); } public function testValidateReturnsTrueWhenJsonValid() { $data = (object)[ 'type' => 'chirp', 'id' => 'uuid', 'attributes' => (object)[ 'chirpText' => 'Test' ] ]; $payload = (object)['data' => $data]; $json = json_encode($payload); $validator = new SimpleJsonApiValidator(); self::assertTrue($validator->validate($json)); } }
  • 33.
    @jessicamauerhan Thank You! Effective CodeReview Lone Star PHP 2017 https://joind.in/talk/2bf9b Feedback & Questions? Welcome & Encouraged! @jessicamauerhan jessicamauerhan@gmail.com jmauerhan.wordpress.com