The anti-patterns in Android codebase and why I hate them

mengdd

Dandan Meng

Posted on June 14, 2022

The anti-patterns in Android codebase and why I hate them

As developers, we work with the code everyday.
Mostly we will be working on some existing code created by others, or as we always like to refer to it as “legacy code”.

Actually, no matter if the codebase is very old or pretty new, there are always some anti-patterns to complain about. Perfect codebase is quite rare.

In this article I want to list something I hate most in any android codebase, only biased opinions from a normal developer. So feel free to disagree.

No. 1 No Kotlin

If a project is purely java and still in an active development state, I don't believe it has the ambition to embrace all the new technologies.

I don't think working on this project is a good choice for younger developers who are just entering the Android world.

I would suggest we migrate to Kotlin at least for new features or new code we are touching, bit by bit.

If we can't do even this, run from this project.

No. 2 No DI framework

Well, it is 2022 already.

For the Android world, Dependency Injection is not a new topic.
In the old days we had dagger, and now Hilt makes things even more easier and simpler.

If you don't like to follow Google's recommendations you could also choose from many third party solutions such as Koin.

There are so many options to choose from and you like neither of them.

The consequences may be:

  • There are many boilerplate codes just to assemble dependency instances.
  • Your singleton instances might be a disaster to look at and maintain.
  • If you are using ViewModel, you are writing a lot of Factories to create ViewModels.

No. 3 object with global variables

This can be another broader topic whether singleton is a pattern/anti-pattern.

Yes, singleton is useful.

Only when you are clear about the boundaries.
When you are sure if it's necessary.
Don’t over use it for everything.

It's even worse when the object has public vars in it.
Everyone at every place can read and write it.
Yes, it's global variables.

It feels like a key to the devil's gate.

You can't image the tricky usages, and it's hard to debug and maintain.

The object is bad for the following reasons:

  • It's hard to be unit tested.
  • Sometimes you have to write ugly code to assign value to it and then set it to null.

It's only nice when there are only some utilities functions inside it.
But in the Kotlin world we also have other choices such as the top-level functions and extensions functions.

No. 4 More than 10 Feature Toggles and A/B Tests

If you are a product manager you might have strong reasons for all the feature toggles and A/B Tests. And I'm not challenging the values of them.

As mentioned from the beginning, I'm just a normal developer who deals with this if-else nightmare.

Even if I totally understand and agree with the business values, it can not stop me from hating them.

Implementing new features with the feature toggle could be trivial work as you need to keep everything as before when the toggle is off.

If developers do this on a large scale, like copying the whole file and giving a different name, there could be some duplications. And other changes (for other features) might need to apply to both versions.

If developers choose to do this on a small scale, like the if-else on several lines. They have to be precise on every change.
There will be multiple if-else.

Implementing compatibility is not fun.
And it generates code smells.
If the developer is very patient, he will add unit test cases for both toggle on and off scenarios. But he may also have missed some conditions, if the old code is not test covered.
And often, there will be no refactoring.
What's the point of refactoring the code we might not need soon?

It is also annoying for QA's work as every feature needs to test more flows, which not only means feature A's on and off, but also feature B on feature A's on and off.

Yup, things would definitely get worse when our feature toggles nested.
They just have the complex relationship you need to explain to QA and yourself. If more than 2 feature toggles are nested, I think it's time to talk to your PO to remove one.

No. 5 Legacy dependencies

Some libraries were really popular and now are deprecated.

I'm looking at you, ButterKnife.

This is a famous one and I believe it’s quite rare to see it from any android project.

But in reality we may encounter others.

Also, there may be some 3rd-party libraries that are not maintained anymore.

if you have this flag in your gradle.properties:

android.enableJetifier=true
Enter fullscreen mode Exit fullscreen mode

You can run this task to check if any of your dependencies are still using the legacy support library rather than AndroidX:

./gradlew checkJetifier
Enter fullscreen mode Exit fullscreen mode

If you find some libraries, I bet they have not been updated for years.

Please evaluate whether you can find a better replacement.

No. 6 Misused Inheritance

It's really painful to see a codebase that has a bunch of base classes for its Activities, Fragments, and ViewModels.

A bunch means several, or, a lot.

For example, between the feature's Fragment and AndroidX's Fragment, there may be 2 or 3 parent fragments that play their mysterious roles.

When a developer wants to add a new Fragment, he might get confused whether to inherit from a base Fragment, and which “base” to use.

And the same for ViewModel. Do you really need a ViewModel to hold the Loading state and another ViewModel to hold the navigation variable?
So a new ViewMoel has to decide if it needs to reuse them all or do things by itself.

Normally, the BaseFragments and BaseViewModels will have some base type defined as generic types. Or even worse, you have BaseUseCase.

Please try to get rid of this inheritance net, it makes navigating in IDE harder than before.

We should minimize the complexity, isn't it?

I hope you always remember composition over inheritance.

No. 7 Interface for everything

You don't need interfaces for everything.

If the interface has only 1 implementation, there is no reason to keep it.

Maybe some developer designed this for good intention, but we only ended up with a lot of boilerplate code.

If the other implementation is for the test, let's review whether it's a real case or just not using any mock framework yet.

No. 8 EventBus Style

EventBus is a famous library.
It is also well-known as an anti-pattern, because it's so free, you can publish one thing and receive it anywhere. Making things unorganized.

The overuse of EventBus in a codebase could be a disaster.
It will decrease the intention to write better structured code.

Sometimes we are writing “EventBus code” even without actually using the Library.
I hope it’s applied only for a few cases, not a common pattern in your codebase.

Summing up

That’s it for now.

Hope this article is helpful to you. You may find we are suffering with the same issues.
Or you get to know why developers in your team are not that happy or struggling with their hair in hand everyday.

Let’s make our life easier.

💖 💪 🙅 🚩
mengdd
Dandan Meng

Posted on June 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related