Brushup: Dependency
Coupling: Mitigate content/common/external/control coupling
Direction: Make dependency acyclic or encapsulate the cycle
Redundancy: Avoid duplication of dependency
Explicitness: Make dependency visible on the definition/diagram
Slide 3
Slide 3 text
Contents of this lecture
- Introduction and Principles
- Natural language: Naming, Comments
- Inner type structure: State, Function
- Inter type structure: Dependency I, Dependency II
- Follow-up: Review
Review > Introduction
Slide 4
Slide 4 text
Why do we need code review?
Readability
- Check from the perspective of other engineers
Logic correctness
- Edge cases, errors and the handlings, race conditions, etc.
- Basically the author's responsibility
Review > Introduction
Slide 5
Slide 5 text
Efficiency of review
Efficiency of the review itself is also important
- Readability is for productivity
- Inefficient review decreases productivity
Review > Introduction
Slide 6
Slide 6 text
Topics
Reviewee (author / who requests a review):
- How to create a pull-request easy to review
- How to address review comments
Reviewer (reader / who conducts a review):
- Principles
- Comment contents
Review > Introduction
Slide 7
Slide 7 text
Topics
Reviewee (author / who requests a review):
- How to create a pull-request easy to review
- How to address review comments
Reviewer (reader / who conducts a review):
- Principles
- Comment contents
Review > Pull-request easy to review
Slide 8
Slide 8 text
How to create a pull-request easy to review
- Clarify the scope of responsibility
- Keep the pull-request size small
Review > Pull-request easy to review
Slide 9
Slide 9 text
How to create a pull-request easy to review
- Clarify the scope of responsibility
- Keep the pull-request size small
Review > Pull-request easy to review > Responsibility of pull-request
Slide 10
Slide 10 text
Responsibility of pull-request
Write a description of a pull-request
- Main purpose, what to achieve
Clarify boundaries of responsibility
- Out-of-scope
- Future plan
Review > Pull-request easy to review > Responsibility of pull-request
Slide 11
Slide 11 text
Why description is important 1/2
Responsibility of reviewers:
- Request/suggest changes if there is rooms to improve
Unlimited change requests may be made
Review > Pull-request easy to review > Responsibility of pull-request
Slide 12
Slide 12 text
Why description is important 2/2
Make it easy to reach a consensus
- Which pull request to apply improvement
- Whether this change moves toward the final design
In addition, use documents such as "design docs"
Review > Pull-request easy to review > Responsibility of pull-request
Slide 13
Slide 13 text
How to create a pull-request easy to review
- Clarify the scope of responsibility
- Keep the pull-request size small
Review > Pull-request easy to review > Small pull-request
Slide 14
Slide 14 text
Pull-request size and review efficiency
Large pull-request review is inefficient
- Takes long time to understand the pull-request
- Requires many iterations to review
Split daily work into several pull-requests
Never create "a pull-request of the week"
Review > Pull-request easy to review > Small pull-request
Slide 15
Slide 15 text
How to split pull-request
- For a large feature implementation
- For an additional task
Review > Pull-request easy to review > Small pull-request
Slide 16
Slide 16 text
How to split pull-request
- For a large feature implementation
- For an additional task
Review > Pull-request easy to review > Small pull-request > Large feature implementation
Slide 17
Slide 17 text
Ways to split a large feature implementation
Development of a feature may take weekly or months
- Cannot be consolidated in one pull-request
- Need to merge unfinished feature implementations
Two ways to split: Top-down, Bottom-up
Review > Pull-request easy to review > Small pull-request > Large feature implementation
Slide 18
Slide 18 text
Top-down approach
Create only the structure first, fill the implementation detail later
1. Create skelton classes to explain the structure
2. Describe the future plan with TODO/pull-request comments
class UserProfilePresenter(
val userProfileUseCase: UseCase
val profileRootView: View
) {
fun showProfileImage() = TODO("#12345: ...")
Review > Pull-request easy to review > Small pull-request > Large feature implementation
Slide 19
Slide 19 text
Bottom-up approach
Create small parts first
1. Implements simple classes/functions without any client code
2. Explain the client code plan with TODO/pull-request comments
data class UserProfileData(val ..., val ...) // TODO(#12346): ...
object UserNameStringUtils {
@Suppress("unused") // TODO(#12347): ...
fun normalizeEmoji(userName: String): String = ...
Review > Pull-request easy to review > Small pull-request > Large feature implementation
Slide 20
Slide 20 text
How to split pull-request
- For a large feature implementation
- For an additional task
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 21
Slide 21 text
Example of additional task 1/3
Assumption 1: Implements a clock display feature
class CurrentClockIndicator(...) {
fun showCurrentTime() {
val clockText = toClockText(...)
// ...
}
companion object {
private fun toClockText(...: Long): String = ...
}
}
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 22
Slide 22 text
Example of additional task 2/3
Commit list of implementation
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 2: Implement a private utility function, `toClockText`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 23
Slide 23 text
Example of additional task 2/3
Assumption 2: A copy of toClockText is found in a existing class
Need to extract it to DateTimeTextFormatter
object DateTimeTextFormatter {
fun toClockText(...: Long): String = ...
}
class CurrentClockIndicator(...) {
fun showCurrentTime() {
val clockText = DateTimeTextFormatter.toClockText(...)
// ...
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 24
Slide 24 text
Pull-request for additional task
Question: How can you make pull-requests?
- Anti-pattern 1: Consolidate into a pull-request
- Anti-pattern 2: Split pull-requests in chronological order
- Solution 1: Finish the additional task first
- Solution 2: Restructure the commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 25
Slide 25 text
Pull-request for additional task
Question: How can you make pull-requests?
- Anti-pattern 1: Consolidate into a pull-request
- Anti-pattern 2: Split pull-requests in chronological order
- Solution 1: Finish the additional task first
- Solution 2: Restructure the commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 26
Slide 26 text
Anti-pattern 1: Consolidate into a pull-request
Commits of a pull-request
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 2: Implement a private utility function, `toClockText`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Commit 4: Extract `toClockText` to `DateTimeTextFormatter`
Commit 4 is totally different && the change becomes extensive
Requires check for missing extractions, etc.
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 27
Slide 27 text
Pull-request for additional task
Question: How can you make pull-requests?
- Anti-pattern 1: Consolidate into a pull-request
- Anti-pattern 2: Split pull-requests in chronological order
- Solution 1: Finish the additional task first
- Solution 2: Restructure the commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 28
Slide 28 text
Anti-pattern 2: Split in chronological order 1/2
Commits of pull-request A
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 2: Implement a private utility function, `toClockText`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Commits of pull-request B
Commit 4: Extract `toClockText` to `DateTimeTextFormatter`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 29
Slide 29 text
Anti-pattern 2: Split in chronological order 1/2
Marge pull-request A and then pull-request B
Causes technical debts
- Code copy is temporarily made
- Pull-request B may be left unmerged due to task interruption
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 30
Slide 30 text
Pull-request for additional task
Question: How can you make pull-requests?
- Anti-pattern 1: Consolidate into a pull-request
- Anti-pattern 2: Split pull-requests in chronological order
- Solution 1: Finish the additional task first
- Solution 2: Restructure the commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 31
Slide 31 text
Solution 1: Finish the additional task first 1/2
Create a pull request for DateTimeTextFormatter concurrently
Condition: Notices code duplication before adding toClockText
Commits of pull-request A
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commits of pull-request B
Commit 4: Extract `toClockText` to `DateTimeTextFormatter`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 32
Slide 32 text
Solution 1: Finish the additional task first 2/2
Rebase pull-request A after merging pull-request B
Pull-request A can be created using toClockText after extraction
Commits of pull-request A
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 33
Slide 33 text
Pull-request for additional task
Question: How can you make pull-requests?
- Anti-pattern 1: Consolidate into a pull-request
- Anti-pattern 2: Split pull-requests in chronological order
- Solution 1: Finish the additional task first
- Solution 2: Restructure the commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 34
Slide 34 text
Solution 2: Restructure the commits
Split, reorder, squash commits
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 35
Slide 35 text
Solution 2: Restructure the commits 1/4
Step 1: Split Commit 4
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 2: Implement a private utility function, `toClockText`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Commit 4_EXISTING: Extract `toClockText` from the existing classes
Commit 4_NEW: Extract `toClockText` from `CurrentTimeIndicator`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 36
Slide 36 text
Solution 2: Restructure the commits 2/4
Step 2: Reorder commits
Commit 4_EXISTING: Extract `toClockText` from the existing classes
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 2: Implement a private utility function, `toClockText`
Commit 4_NEW: Extract `toClockText` from `CurrentTimeIndicator`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 37
Slide 37 text
Solution 2: Restructure the commits 3/4
Step 3: Squash the middle three commits
Commit 4_EXISTING: Extract `toClockText` from the existing classes
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
--squashed--
--squashed--
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 38
Slide 38 text
Solution 2: Restructure the commits 4/4
Step 4: Create two pull-requests
Pull-request B
Commit 4_EXISTING: Extract `toClockText` from the existing classes
Pull-request A
Commit 1: Create a skeleton class, `CurrentTimeIndicator`
Commit 3: Implement `CurrentTimeIndicator.showCurrentTime`
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 39
Slide 39 text
Note to keep pull-requests small
Reach consensus before implementing
- Which task is to be done, when, and in which pull-request
- Whether we allow temporary debts and how to resolve them
Discuss changes as soon as possible when they are found
Review > Pull-request easy to review > Small pull-request > Additional task
Slide 40
Slide 40 text
Create a pull-request easy to review: Summary
Clarify the responsibility of a pull-request
- Main purpose, what to achieve
- Out-of-scope, future plan
Keep a pull-request small
- For a large feature: top-down and bottom-up approaches
- For an additional task: task ordering, commit restructuring
Review > Pull-request easy to review > Summary
Slide 41
Slide 41 text
Aside: Application to a commit
Apply the same ideas to a commit
- Responsibility of a commit
- How to split a commit
Review > Pull-request easy to review > Summary
Slide 42
Slide 42 text
Topics
Reviewee (author / who requests a review):
- How to create a pull-request easy to review
- How to address review comments
Reviewer (reader / who conducts a review):
- Principles
- Comment contents
Review > Addressing review comments
Slide 43
Slide 43 text
Important point in addressing comments
Do not merely address comments
Review > Addressing review comments
Slide 44
Slide 44 text
Do not merely address comments
- Understand the comment's intent
- Think why the reviewer asked/misunderstood
- Apply to other places as well
Review > Addressing review comments
Slide 45
Slide 45 text
Do not merely address comments
- Understand the comment's intent
- Think why the reviewer asked/misunderstood
- Apply to other places as well
Review > Addressing review comments
Slide 46
Slide 46 text
Understand the comment's intent
Do not merely paste suggested code in a review comment
- Understand what the key idea is
- Attempt further improvement
- Validate: edge cases, exceptions, race conditions, etc.
Review > Addressing review comments
Slide 47
Slide 47 text
Do not merely address comments
- Understand the intent of the comment
- Think why reviewer asked/misunderstood
- Apply to other places as well
Review > Addressing review comments
Slide 48
Slide 48 text
Think why reviewer asked/misunderstood
Question/misunderstanding can be caused by unreadable code
Example of comment: "Why is the null case checked here?"
- Remove the null check if unnecessary
- Write in-line comment to explain why
- Extract as a local value to explain (e.g., isUserExpired)
Review > Addressing review comments
Slide 49
Slide 49 text
Do not merely address comments
- Understand the comment's intent
- Think why the reviewer asked/misunderstood
- Apply to other places as well
Review > Addressing review comments
Slide 50
Slide 50 text
Apply to other places as well
Find similar code and address the comment
Example of comment "Add @Nullable to this parameter":
- Apply to other parameters or return value
- Apply to other changed functions
- Apply to future pull-requests
Review > Addressing review comments
Slide 51
Slide 51 text
Addressing review comments: Summary
Understand what the intention of a review comment is
- Find the comment's key idea and reason
- Attempt future improvement
- Try to apply it to other parts
Review > Addressing review comments > Summary
Slide 52
Slide 52 text
Topics
Reviewee (author / who requests a review):
- How to create a pull-request easy to review
- How to address review comments
Reviewer (reader / who conducts a review):
- Principles
- Comment contents
Review > Reviewer principles
Slide 53
Slide 53 text
Principles for reviewers
Respect reviewees
- Never verbally abuse nor deny personality
- Subject of review: code/specification/process (!= reviewee)
Keep review process efficient
- Do not use resources indefinitely
- Do not cut the required time
Review > Reviewer principles
Slide 54
Slide 54 text
Principles for respect and efficiency
- Do not neglect review requests
- Reject problematic pull-requests
- Do not be too deadline-conscious
- Consider other options of request/suggest
Review > Reviewer principles
Slide 55
Slide 55 text
Principles for respect and efficiency
- Do not neglect review requests
- Reject problematic pull-requests
- Do not be too deadline-conscious
- Consider other options of request/suggest
Review > Reviewer principles
Slide 56
Slide 56 text
Do not neglect review requests
Define a reply time limit as a rule
- e.g., 24 hours of working days
Question: What is the "worst" behavior regarding review deadlines?
Review > Reviewer principles > Do not neglect
Slide 57
Slide 57 text
Reactions to review requests
GOOD:
- Reviewing soon or on time
- Redirecting to other reviewer
- Replying as "I cannot review now" or "I'll be late"
NOT SO GOOD:
- Ignoring a review
WORST:
- Replying "I'll review" without review
Review > Reviewer principles > Do not neglect
Slide 58
Slide 58 text
When you cannot review
If you are busy, reply explaining so
The reviewee can:
- Wait for the review if it does not block other tasks
- Find other reviewers if it is an urgent pull-request
Review > Reviewer principles > Do not neglect
Slide 59
Slide 59 text
Principles for respect and efficiency
- Do not neglect review requests
- Reject problematic pull-requests
- Do not be too deadline-conscious
- Consider other options of request/suggest
Review > Reviewer principles > Reject problematic pull-requests
Slide 60
Slide 60 text
Reject problematic pull-requests 1/2
Do not spend too much time reviewing a problematic pull-request
Review > Reviewer principles > Reject problematic pull-requests
Slide 61
Slide 61 text
Reject problematic pull-requests 2/2
Ask the reviewee to re-create without reading the details
Too large:
- Ask to split
Has critical issues on purpose, assumption, or structure:
- Ask to create skeleton classes or have casual chat
Review > Reviewer principles > Reject problematic pull-requests
Slide 62
Slide 62 text
Principles for respect and efficiency
- Do not neglect review requests
- Reject problematic pull-requests
- Do not be too deadline-conscious
- Consider other options of request/suggest
Review > Reviewer principles > Reject problematic pull-requests
Slide 63
Slide 63 text
Do not be too deadline-conscious
Do not lower the approval threshold just because of the deadline
- Let the reviewee explain if they want to merge soon
- Look for alternatives (simplify specifications, postpone)
Exc.: critical feature to business strategy, high impact bug-fix
- Requires tests and agreements/task-tickets on future plan
- Reviewers should always be calmer than reviewees
Review > Reviewer principles > Do not be too deadline-conscious
Slide 64
Slide 64 text
Principles for respect and efficiency
- Do not neglect review requests
- Reject problematic pull-requests
- Do not be too deadline-conscious
- Consider other options of request/suggest
Review > Reviewer principles > Other options of request/suggest
Slide 65
Slide 65 text
Consider other options of request/suggest
The answer is not only showing the answer
To balance the reviewer's load & encourage the reviewee's growth:
- Ask questions rather than spending time on unreadable code
- Only point out the problem and ask to look into the cause
- Write comments only to share knowledge
Review > Reviewer principles > Other options of request/suggest
Slide 66
Slide 66 text
Principles: Summary
Respect, and review efficiently
- Only reply if you cannot review
- Do not spend too much time on problematic pull-requests
- Consider what an efficient and effective comment is
Review > Reviewer principles > Summary
Slide 67
Slide 67 text
Topics
Reviewee (author / who requests a review):
- How to create a pull-request easy to review
- How to address review comments
Reviewer (reader / who conducts a review):
- Principles
- Comment contents
Review > Comment contents
Slide 68
Slide 68 text
What should be commented 1/3
Anything you noticeʢUse tools if possibleʣ
- Coding style, coding conventions
- Language- and platform-specific idioms
- Test code, what are checked on operation test
- Pull-request and commit size/structure
Review > Comment contents
Slide 69
Slide 69 text
What should be commented 2/3
Anything you notice
- Objective and scope of a project/task
- Balance between value and complexity
- Bugs and security issues
- Changes in performance and footprint
Review > Comment contents
Slide 70
Slide 70 text
What should be commented 3/3
Everything talked in this presentation series
principles, naming, comments, state, function, dependency
Review > Comment contents
Slide 71
Slide 71 text
Code review case study
Example to add a parameter on an existing function
fun showPhotoView(
photoData: PhotoData
) {
if (!photoData.isValid) {
return
}
...
Review > Comment contents > Case study
Slide 72
Slide 72 text
Code review case study
Example to add a parameter on an existing function
fun showPhotoView(
photoData: PhotoData,
isInEditScreen: Boolean
) {
if (!photoData.isValid) {
if (isInEditScreen) { showDialog() }
return
}
...
Review > Comment contents > Case study
Slide 73
Slide 73 text
Code review case study
Naming: Explain "what it does" rather than "where it is called"
isInEditScreen
→
showsDialogOnError
Review > Comment contents > Case study
Slide 74
Slide 74 text
Code review case study
Naming: Explain "what it does" rather than "where it is called"
fun showPhotoView(
photoData: PhotoData,
isInEditScreen: Boolean
) {
if (!photoData.isValid) {
if (isInEditScreen) { showDialog() }
return
}
...
Review > Comment contents > Case study
Slide 75
Slide 75 text
Code review case study
Naming: Explain "what it does" rather than "where it is called"
fun showPhotoView(
photoData: PhotoData,
showsDialogOnError: Boolean
) {
if (!photoData.isValid) {
if (showsDialogOnError) { showDialog() }
return
}
...
Review > Comment contents > Case study
Slide 76
Slide 76 text
Code review case study
Dependency: Relax control coupling caused by the parameter
- Extract function call of showDialog
- Return success or failure as a boolean value
Review > Comment contents > Case study
Slide 77
Slide 77 text
Code review case study
Dependency: Relax control coupling caused by the parameter
fun showPhotoView(
photoData: PhotoData,
showsDialogOnError: Boolean
) {
if (!photoData.isValid) {
if (showsDialogOnError) { showDialog() }
return
}
...
Review > Comment contents > Case study
Slide 78
Slide 78 text
Code review case study
Dependency: Relax control coupling caused by the parameter
fun showPhotoView(
photoData: PhotoData
): Boolean {
if (!photoData.isValid) {
return false
}
...
Review > Comment contents > Case study
Slide 79
Slide 79 text
Code review case study
Dependency: Relax control coupling caused by the parameter
fun showPhotoView(photoData: PhotoData): Boolean {
if (!photoData.isValid) {
return false
}
...
return true
}
Review > Comment contents > Case study
Slide 80
Slide 80 text
Code review case study
Dependency: Relax control coupling caused by the parameter
On edit screen:
val isViewShown = showPhotoView(...)
if (!isViewShown) { showErrorDialog(...) }
On other screen:
showPhotoView(...)
Review > Comment contents > Case study
Slide 81
Slide 81 text
Code review case study
Comments:
Comment if a function has both side-effect and return value
/**
* Shows a photo at the center if the given photo data is ...,
* and returns true.
* Otherwise, this returns false without showing the view.
*/
fun showPhotoView(photoData: PhotoData): Boolean {
if (!photoData.isValid) {
return false
}
Review > Comment contents > Case study
Slide 82
Slide 82 text
Comment contents: Summary
Comment whatever you notices
- From coding style to the project's objective
- Sometimes needs multiple review iterations
Please "review" this code readability presentation series
Review > Comment contents > Case study
Slide 83
Slide 83 text
Summary
Review is important for code readability
Reviewee:
- Make pull-requests small and structured
- Understand the intents of review comments
Reviewer:
- Respect, and review efficiently
Review > Summary