Tomáš Repčík - 29. 9. 2024

Building Better Android Apps One Code Review at a Time

Enhance Your App’s Quality and Maintainability

Everyone thinks their ending result of programming is the best, however, that is only sometimes the case.

We are still human and we make mistakes. Another pair of eyes can reduce errors and eliminate the accumulation of technical debt and incorrect practices.

Every development team is different and they should do the work as it works for them. Do not enforce anything that is causing you more trouble than benefit or practice which exists just for its existence.

This article will describe Android-specific tips, but If you are interested in more general approach, you can read about it in this article.

When I make the review, I go from high-level domain to low-level items, so let’s start.

1. Architecture

Random 3rd party packages

It is tempting to use random 3rd party packages to solve all the problems, but they can pose more problems than usual benefits.

You impose limitations of the package on your current implementation like deprecated implementations, heaviness of solution, duplication of code, maintenance and licensing issues. Sometimes, the package can be a source of vulnerabilities and potentially malicious behaviour.

For example:

In many cases, it is worth it to implement it on your own. You have full control over the functionality and full ownership.

Before usage, look at credibility of the package, if it is actually used by people, actively maintained and if the license is permissible for your usage.

Coding patterns

A project usually has its philosophy in terms of architecture and the main pattern used. It can be MVVM/MVI/MVP and others…, and it should be followed across the project.

Every architecture imposes certain rules about which layer is responsible for what. Usually, UI, business logic and repositories are separated. In the case of mixing the layers, code change should be required.

Here are a couple of examples:

Good rule of thumb is, if you are able to test your logic in unit tests with mocking of native repositories only, then you achieved good level of separation.

2. Functionality

If the architecture seems right, we can go level further and inspect the actual implementation of the feature.

We should always check if the code does, what is desired from the requirements.

Asking for demo is sometimes the simplest thing, what you can do, because code can tell one thing, but actual behaviour could differ from the expectation.

UI

The final UI should match the proposed design and changes should be allowed only after discussing the proposed change. Sometimes it is better to change the design a tiny bit to make the implementation much more straightforward.

Check:

Error handling

To achieve a happy path to work is one part of success. Useful error handling in another part of the code, which should be robust.

Check for handling of:

Try to prevent:

3. Complexity

Let’s assume the functionality now works and handles everything that is required. However, this does not mean that it is correct.

If you can read through the implemented feature and it is clear, what has been implemented, then it is a good thing. However, if you struggle to comprehend, what is going on, then it is a good candidate for improvement.

The feature might be too fragmented or use too many dependencies.

A couple of tips:

Simplicity is over complexity. Showing easier way is learning opportunity.

Is it speculation or a solution?

We try to prevent our app from failing/slowing down and that results in sometimes adding functionalities for “just in case” reasons, which are for the most part redundant.

With higher complexity, you can achieve some advantages, but they are often minimal in comparison to future maintenance and speed of the code.

Opting for tinderbox solution is sometimes preferable instead of using flamethrower.

Monitor Performance Only When Necessary

You will always find a place for performance improvement. You can always squeeze a couple of milliseconds from the algorithm. However, do not optimize if no one complains. You might be wasting time, which should be invested somewhere. where is the actual value.

Good rules of thumb:

4. Testing

Tests are part of development, which can save you more time in the future than you invested in them. Do not treat tests as second-class citizens in your codebase.

Check if:

Here are a couple of articles related to testing:

5. Code styling

Last, but not least is the actual code styling. You can have beautifully styled code, but if the architecture is not good, you will end up rewriting it anyway.

Even tests are above, because, if you are not able to write tests for your implementation, then there is something wrong with it.

What to look for:

All linter findings should be addressed at the end of code changes.

Code of behaviour:

Conclusion

Core review is the point of development when we decide if the changes are valid for the project. If you agree, you are responsible for them too and take time to review the code properly. At some point, you might be the one fixing the mess, which you agreed to that is fine.

Hopefully, these tips will make it easier for you to go through the code review and improve the codebase of your team.

Thanks for reading and do not forget to follow!

Subscribe for more
LinkedIn GitHub Medium Threads X Bluesky