Features, but faster: the life of an Android developer at Badoo

Anatoliy Varyvonchyk
Bumble Tech
18 min readJul 23, 2019

--

Practically speaking, my colleagues and I often need to test an idea as fast and as simply as possible. There’s no point spending lots of effort on implementation because we know that if an experiment proves unsuccessful, we are going to jettison the code in question.

Here I will be showing you, based on real examples, what our approach is in such circumstances and principles to help you select one solution over another. Analysis of these examples should help you to understand how we think, and especially how shortcuts can sometimes be found that speed up development.

I will explore 4 main areas:

1. Principles of the Badoo development approach.

2. Examples from practical experience

3. Design system

4. When to apply the principles described.

Principles of our approach to development

Our applications, Badoo and Bumble together have more than millions of users so we can never roll out new functionality until we are confident that users are going to like it and find it useful.

Several factors influence our development approach.

Use of А/В tests

There are dozens of A/B tests active on our mobile platforms today — and several hundred completed tests. Correspondingly, if you compare the Badoo application on two different devices there will be differences between them, but these might not be apparent at first glance.

What do we need А/В tests for? It is important to understand that things product managers may consider essential and even things that seem obvious to us, may not always prove useful in reality. We sometimes find that we need to remove code written just a month or two previously. It makes sense to test an idea for some new functionality, to see whether it’s suitable or not. And then, on the condition that users actually like the given functionality, it merits investing time in developing it.

Cutting development costs

It goes without saying that we want everything to work fast and look elegant. However, this can’t always be achieved in a short timeframe. Sometimes things take many days. So, to avoid such issues, we try to assist product managers by assessing the costs of tasks in advance and identifying what will be complicated to do and what will be easy.

The ‘majority of users’ rule

Let’s imagine that you have functionality which works perfectly in all scenarios on all devices, apart from a group of users with Chinese devices on which it doesn’t work as expected. In this case, it might not be worth prioritising this fix, because there will be other more important tasks.

How we speed up development

Let’s look at several examples which illustrate how these principles work. I am going to show you some real cases which we encountered during the course of our work, as well as considering some options for solutions.

First, let me suggest that you consider how you might solve a given case. And then I will explore each option, explaining why it is, or isn’t, suitable in our case.

Example 1. Progress button

We need to display to the user the progress of credit accumulated from 0 to 1 using a battery button with rounded corners.

What are the possible solutions?

Option A. We don’t need this icon. We need to ask a designer to redo the functionality. Just display some text.

Option B. Use bitmap masks. If we combine these properly, we get just what we want.

Option C: Simply take several icons, hardcode them on the client and display one of them.

In our case we opted for solutions B and C. Let’s take a closer look.

Why not option A? We can solve this particular task; it isn’t complicated. This is the very design we use in iOS and on mobile web. There is no reason to reject this and say that we won’t do it this way and we need to come up with another design.

Bitmap masks (option В) are an ideal solution for this task. Drawing a rectangle with rounded corners is easy to do. Drawing an ordinary rectangle with the necessary percentage is easy as well. All that remains is to combine them and to display the correct settings. After this the two corners on the left-hand side of the rectangle will disappear.

The code it will look like this:

I have removed most of the code. There is more about bitmap masks in the following article: https://badoo.com/techblog/blog/2016/10/17/masking-bitmaps/.

This solution satisfies our requirements 100%, i.e. enables us to show progress from 0 to 1. The only downside is that, if you haven’t done this before, you will need to spend some time getting to grips with bitmap masks. Also, you will need to play around with them, consider edge cases and test them. This will probably take you about 4 hours or so.

Option С. With this option we just take several fixed-type icons and choose one of them, depending on the state of progress. For example, if progress is less than 0.5, then an empty icon will be displayed. Obviously, this solution does not satisfy requirements 100%. However, in order to implement it you only need to write five lines of code and obtain three icons from a designer.

Moreover, this solution is optimal in circumstances where you are seriously short of time (as in our case, when we were releasing livestream functionality). It’s fast; you simply roll it out and replace it with the proper, correct solution in the next release. And this is basically what we did at the time.

Example 2. Field for entering a telephone number

The following example involves entering a telephone number. The distinctive aspects here were:

  • Country code is positioned slightly to the left
  • Country code cannot be deleted
  • There is indentation
  • Country code cannot be clicked on.

Let’s think about how we could implement this.

Option A: Write a custom TextWatcher that implement the necessary logic. It will retain the country code and the space and manage the position of the cursor.

Option B: Split this component into two independent fields. From the perspective of UI this will be one and the same component.

Option C: Request another design to make things simpler for us.

We decided to implement option В. Let’s consider this in more detail.

The first thing we tried was requesting another design (option С). However, the product managers insisted on sticking to the original idea. And, if a business insists on particular functionality, it is our job to implement it.

A custom TextWatcher (option А) only appears to be a simple solution at first glance, but in actual fact a large number of edge cases occur which need to be dealt with. For example, you need to do the following:

  • Somehow ‘intercept’ clicks on the country code, in order to alter the position of the cursor
  • Also retain the indentation
  • Block deletion so that the user cannot remove the space or the country code.

Whilst doing all that is certainly possible, but it’s quite complicated. There must be a simpler option.

And we found it:

We simply divided this component into two parts: TextView and EditText. In terms of programming, we imposed a background on TextView in such a way as to obtain the design which the product managers were expecting.

The one thing that is worth thinking about is that, in Android, the default width of the lower line increases when EditText is in focus. But it was easy for us to alter the focus and change the country code background. There is nothing complicated about it:

This solution has a number of advantages:

  • You don’t need to deal with clicks on the country code
  • There is no need to do anything with the cursor — it will always be in a separate field
  • Far fewer edge cases and problems occur with this implementation.

Example 3. Auto-complete problem

As you can see from the below animation, auto-complete doesn’t work the way you would like.

We would prefer everything to look as it does on the below animation.

Option А: This seems to be a rare case that no one is going to fix. Why don’t we do the same?

Option В: A custom TextWatcher would be far more suitable and would solve all our problems.

Option С: Remove the limit on the number of symbols (as you can see from the animation, this component has a set number of symbols). We are going to send the server the entire telephone number along with the country code and then we will let the server decide whether or not the number is valid.

Option D: Take N symbols from the end

We decided on option D.

Option А. I had a look at several major applications. It would indeed appear that this is something no one is going to fix.

Nevertheless, in future more and more fields are going to be completed using autofill. The sooner you solve this problem, the more loyal your users will be and the nicer it will be to use the application. For example, I really like it when I can navigate an entire screen with two clicks.

Option В. In this case it is actually simpler to implement a custom TextWatcher, since there are not as many extreme scenarios as with the previous example. You can calmly intercept inserted text. There is just one minor problem: some countries have local aliases. For example, +44 and 0 are equivalents.

A custom TextWatcher is not going to be any good in this case. Instead, you’d need to write additional logic as well as request the server to return all possible local aliases for the country in question. To do this, you’d need to introduce changes to the protocol for communicating with the server and then implement this functionality on the server. This would take more time than it would to do something on the client. There must be a simpler solution (we will come to that).

Option С. Remove the limit on the number of symbols and the server will validate. This is an excellent option. It isn’t a problem if the country code is displayed twice. If the user moves on to the next step and the telephone number is correctly identified, then basically there won’t be any problems.

There is, however, one ‘but’. Imagine that a user, instead of using autofill, simply enters their telephone number manually. In this case, if there is a limit to the number of symbols, the user will find it much harder; if they accidentally repeat a symbol, when they have finished they would see that the last symbol has not been displayed. So, we discounted this method.

Option D. We decided that the best solution was to use N symbols from the end.

We have a maximum length for a telephone number entered. We write one simple class, which is encapsulated and can be used in other places. Also, any other developer seeing the code will quickly be able to see what it is all about. However, two problems arise.

Firstly, there are countries where telephone numbers are not always the same length. i. In such cases, our solution will display an additional digit from the country code. Secondly, if a user using autofill, enters the country code for another country, the same thing could occur. It seems to us that the second case would be rare, because the server will initially return a telephone number according to the country where the user is located. However, if we find that this is a problem, we will have to change the protocol on the server so that it returns a list of all the numbers straightaway, and write additional logic (we don’t consider this to be essential at the present time).

Example 4. Component for entering a date

Designers and product managers want to see a mask for entering the date, as follows:

Let’s think how we can implement this.

Option A: Just do it. The task looks simple and it is easy to solve; there shouldn’t be any problems.

Option В: Use a masks library. This suits us in this situation.

Option С: Block users from managing the position of the cursor. That way we simplify requirements somewhat and it will make it simpler for us to implement this functionality.

Option D: Use the standard component for entering a date, which is on Android and which we have all seen.

We decided on option C.

[A) Just do it, I can’t see any problem

B) Use a masks library

C) Block users from managing the position of the cursor (simplify requirements)

D) Use a standard component for entering a date]

Option А. It seems like a simple task. We probably aren’t the first ones to implement this functionality. Why not have a look on the internet to see if there is a suitable solution there?

Let’s take this solution, add it to the code and run it. So, we start testing:

At first glance it seems that this is more or less what we are looking for although it has to be said that there are problems to do with the cursor jumping to the end after each change. But when we start to test more carefully we quickly realise that things aren’t as good as we thought, and there are scenarios which haven’t been considered.

It all needs to be reworked. It would be preferable to avoid all this because it creates extra work for testers and then extra work for us, fixing bugs etc.

Option В. How about a ready-to-use library for decoro or input-mask-android masks? These ones have tested out all the scenarios and you can simply reuse them and relax. If you already have a library for masks in your project, or are prepared to add one, then this is an excellent solution.

We didn’t have a library. And it seemed to us to be over the top to drag a library into our project just for the sake of one little component which won’t be used anywhere else.

Option D. Use a standard component for entering a date.

This seems to be the most sensible solution. Everything is fine in this case, apart from one little drawback. When you open up this component there is already a preset value, a given valid date. If you enter a random valid date in order to move on to the next step, for example, 1 January 1980, you get millions of users who were born on that day. Or you get lots of similar errors, namely the user cannot register because they are too old or too young.

For this reason, at the time, we decided to forego having a standard date picker dialog on the Badoo registration form. The number of invalid date errors was subsequently reduced three-fold.

And another minor drawback. It would seem that only advanced users know how to move from the first view to the second:

If it isn’t only advanced users who are using your application, then users are going to be ploughing their way through the calendar month-by-month to find the date they need.

That is why we decided that option А isn’t that bad. It just needs to be reworked and slightly simplified:

The drawbacks of option A started to manifest themselves when the user changed the position of the cursor. And so we thought, “Why even give people the option of moving this cursor?” and so we simply blocked it, preventing them from doing so.

That is how we resolved all the problems. The product managers got the implementation they were looking for. And if, in the future, they decide that they do actually need to give users the option of removing symbols from the middle of a date, then we can do that for them.

Example 5. Titles on a video-streaming screen

When running video-streaming, product managers wanted to display tooltips for teaching users how to use functionality.

At the time of implementing the feature, we had six types of tooltips. At any one time there should not be more than one tooltip displayed on the screen. The tooltips arrived from the server dynamically at random times. Some were supposed to be repeated. If a tooltip is displayed but the user does not click on it, the tooltip should display again after N minutes.

This all seemed quite complicated to implement. We requested several things from the product managers.

Firstly, to add a classifier to prioritise tooltips. In any event there will be situations in which two tooltips both want to display at the same time, and we need to select one of them. That is why we need to know priorities. Secondly, we requested a minor simplification: to support the timer only for the top-priority tooltip.

Previously, repeat timers for the tooltips worked independently:

But we requested timer support only for the current top-priority tooltip:

Correspondingly, the timer only worked for tooltip 1. As soon as tooltip 1 displayed, it was removed and the following one began to be processed.

Thus, we simplified requirements: it not only became simpler for us to implement a feature but it also became simpler for the testers to test it. As a result, we realised that this solution suited everyone.

Example 6. Reordering photographs

This is the design we received:

We came to the conclusion that this is quite complicated to implement, that it would take 1 week to develop. So we thought, “Why should we even do this, when we don’t know for sure that this is something users need?” We suggested running a simplified version initially so we could assess how much this feature was in demand.

It turned out that users were interested in this functionality. Next, we perfected photo reordering to its status at the initial design stage.

Conclusions:

  • We protected ourselves and the company from the risk of spending too much working time on a feature which might prove useless;
  • The product managers’ requirements were satisfied in full.

Example 7. Component for entering a PIN code

As mentioned earlier, we are not only developing the Badoo application but we have four other applications with entirely different designs. And in all three applications we use one and the same component for entering a PIN code:

From a UX point of view the component should behave in the same way. Nevertheless, different applications have different fonts, indentation and even different backgrounds. It would be nice not to copy and paste this into each application, but rather to reuse material. This is something which a design system can help us with.

A design system is a set of UX rules about how certain components should behave. For example, in our case it is clearly laid down that each button has to have certain statuses and that it has to behave in a certain way.

You can find out more about the design system in the following series of articles “From zero to Cosmos” by Cristiano Rastelli:

Meanwhile let’s get back to the component for entering a PIN code. What would we like to have?

  • Appropriate keyboard behaviour
  • The option of fully customising UI so that it has a different appearance in different applications
  • To get a standard data stream from this component, as from an ordinary EditText.

What solution options did you come up with?

Option А: Use four separate EditTexts, where each PIN element will be a separate EditText.

Option В: Use one EditText, add in a bit of creativity — and get what you want.

We went for option В.

Option А. Having four separate EditTexts creates problems. Android adds additional indentation on all sides, which we will need to deal with appropriately. You also need to implement a backwards ‘tap and hold’, so the user can delete the whole PIN code. We would need to work manually with focus and to deal with symbol deletion. This seems quite complicated.

So, we decided to be a bit cunning and created an invisible EditText of dimensions 0 by 0 to be the data source:

Each digit of the PIN code will be added programmatically. As a result, we can draw any UI we like, and set any indentation we like etc. Once the user clicks on the component, we put the focus into our EditText. This way we ensure the keyboard functions properly.

Besides this, we go for changing the text of the invisible EditText and displaying it on the UI. After this it is easy to export a data stream from this component. Basically, we reused the standard Android EditText and just added a little extra logic.

Conclusions

These principles are not always applicable. Let me specify the conditions under which they will work well:

  • The developer is able to influence the functionality. Otherwise, they are simply left to carry out the task set.
  • The developer works at a product company where features are actively delivered and are released quickly, and also where hypotheses vis-à-vis these features are tested quickly. Under these conditions the principles we are discussing will work in full, since, again, we cannot initially be 100% certain which updates users will like and which ones they won’t.
  • The developer has the possibility of breaking tasks down. These principles represent the logical solution in a situation where product managers and developers have two-way interaction, allowing both sides to find what can/needs to be reworked.
  • Outsourcing. In rare cases a customer may be interested in the offer of, for example, shortening the time it takes to perform a task by simplifying part of the functionality.

How can we apply these principles? Unfortunately, outside the context in question it is difficult to give recommendations. However, the following pointers might be useful.

You may have problems with UI/UX, as in most of the examples, and you may have problems with business logic, as in the example with the tooltips. You need to try to break your task down into several smaller subtasks, and then evaluate them.

Then you will be able to know for certain whether or not to expect problems. You can then discuss potential solutions with colleagues. Perhaps something can be simplified. Or, maybe your colleagues have a simple solution you were unaware of. Next, agree an alternative solution with the product managers. If they agree then you can go ahead and implement your suggestion.

I would like to add that everyone makes mistakes sometimes. It is possible that product managers have set a task which is not actually going to solve their problem. Maybe designers may have sent you a design which is intended for iOS. Perhaps the protocol for communication between the server and the client would be totally inconvenient for the client. You need to talk about all of this, it needs to be discussed and there needs to be feedback. Acting in this way will increase your own value as a developer and your usefulness to the company — a win-win scenario for both parties.

Feel free to get in touch with me on Github or Telegram.

P. S. No doubt there are also other possible solutions to the tasks above. You might be aware of solutions which are better than the ones I have suggested. The aim of this article has been to demonstrate how real-life decisions were made, and what our logic was when we did so. If you know of some better solutions to these tasks, please do share them in the comments.

--

--