Code Reviews
Code Reviews
This article documents how to review code in One App iOS. Helpful for new and remote employees to get and stay aligned.
Philosophy
The code tells a story
Don’t make the reviewee cry
Value others time
Language: You review the code not the developer
Don’t put comments on the general PR if possible
Performing the code review
How to Review
General code smells
Guide (MVVM Architecture)
General module folders structure
Module dependencies
Scenes
Scene coordinator
Scene ViewController
Scene viewModel
Use cases
Guide (old architecture)
In general
Coordinators
Presenters
Other things to check in presenters
Use cases
Philosophy
Why are code reviews important?
When you review a piece of code you should understand the functionality just reading it. If don’t, you usually have two ways of thinking about it:
I’m stupid, so probably this is ok, I will approve it. (that impostor syndrome again)
I don’t understand this code and it should be understood by a junior developer. It can be improved.
The second one must be the attitude to follow. Complex abstractions, overengineering, general and meaningless names, are a self-sabotage for
you and your workmates in the future.
Reviewing is helping others, some times junior developers. Talk offline if it is necessary. Sometimes the problem is bad communication, poor
requirements or the scope of the problem.
If there is official documentation in Confluence about the review comment, add the link, this helps spreading the official conventions and good
practices.
Most developers don’t review their own code, but coworkers aren’t your personal QA. You are responsible for your code quality. Review yourself
with the same criteria that other’s code.
Review the code, not the person. This must be considered in the language used. Don’t say “this is wrong”. Ask questions. Avoid negative
comments.
Examples:
Put comments in the diff in the appropiate point, not in the whole diff. When the comment is solved, be kind and write down an outcome (if you
have time). The comments are the history of its resolution and can be valuable in the future.
How to Review
In general it should use a template for the new module. There should not be a folder “standard” because it is from and old separation between
“standadard” and “legacy” modules that was deprecated.
Module dependencies
There will be a module/dependencies folder with two resolver files. One for local dependencies and one for external dependencies. See iOS -
Dependency injection (DI)
Things to check:
The module has scenes (see iOS - Scenes ) there is an standard structure that must be followed.
Scene coordinator
See the coordinator types in the architecture in the app navigation context, and general coordinator info
Things to check
Usually we will have the following common implementation with an init and the lazy dependencies.
Scene ViewController
The view controller should be correctly related to the view model and the dependencies resolver.
Things to check:
Scene viewModel
The view model uses a state mechanism (see iOS - ViewModel ) and at least a basic state enum and observable must be offered.
Things to check:
A state enum
Use cases
Check list
Don’t import UIKit, UI or in general any device dependant API. Use cases should be only bussiness logic
Use cases returns a publisher with a representable. Never a DTO. (see iOS - Data Objects )
In general
Coordinators
protocol PLBeforeLoginCoordinatorProtocol { }
...
extension PLBeforeLoginCoordinator : PLBeforeLoginCoordinatorProtocol {
...
}
init(dependenciesResolver: DependenciesResolver,
navigationController: UINavigationController?) {
self.navigationController = navigationController
self.dependenciesEngine = DependenciesDefault(father:
dependenciesResolver)
self.setupDependencies()
}
func start() {
...
}
}
Note
dependenciesEngine property is usually private. It make no sense a internal because it is the default access level.
A section with dependencies registration in a private extension
Presenters
...
Usually with life cycle methods like viewDidLoad() and more specific methods.
The presenter implementation
init(dependenciesResolver: DependenciesResolver) {
self.dependenciesResolver = dependenciesResolver
}
}
Note:
final modifier
the dependencies resolver is inyected
the presenter has a weak reference to the scene view.
A private extension with more implementation
Use cases
Note:
is marked final
Naming, ending in UseCase
The recommendation is defining a struct for Input and Output
Include dependenciesResolver in the init method
...
var dependenciesResolver: DependenciesResolver
Other considerations