share

ENGINEERING

5min read

Maintaining Open-Source RxAndroidBle Library (API development)

In the fourth part of the series devoted to maintaining RxAndroidBle library, we will discuss API development. If you want to come back to the introductory article, you can find it here. We’ve got API ready but we need to develop it.

Semantic Versioning (X.Y.Z)

This pattern is very handy when it comes to describing both to the user and the developer what may or may not change in between different versions of code. Let’s start from the end:

  • Z stands for the patch version. If this number changes in between versions of the code, it only means that the implementation detail changed but no public API was touched. It should be possible to simply switch the dependencies even in the already compiled code and run it successfully without any other actions.

  • Y is the minor version. Increasing this number allows the developer to add new API in the public interfaces of a module / library. All previously available APIs need to be in place although ABI may be changed (to see what ABI is just look below). A recompilation is needed.

  • X—the major version. Changing the major version means basically that users upgrading to this revision may need to sit down and rewrite some parts of their code to make it compile again. Previously available functions could not be available anymore.

For those who do not know—ABI stands for Application Binary Interface and can be considered an API for the computer. When users type for instance "Some String".substring(1) they use the API—when the computer compiles this line, it will translate the API to the ABI—the byte address of the method / function to be called. Binary interfaces also include types of data that are available—an int is not binary compatible with a char even though both of them contain numeric values. In Java world, changing the invocation of a method from void doSomething(int a) to void doSomething(int... manyAs) while being API compliant (invocation of both could look like doSomething(1)) is not the same when translated to the ABI. Does it mean that the developers are stuck with the need to support old, legacy API then? Yes, at least for some time. That is why the developers should understand the importance of designing the API before the official release and ask themselves a few questions:

  • What should be exposed as a public API (which will be needed to support)?

  • How to expose a smaller surface of the API (will it still get the work done)?

  • Will it be possible to translate the legacy API to the new one internally? (to support only the new one internally)?

Sometimes when developing a library you can make use of the findings from other fields, such as product development.

MVP or Minimal Viable Product could be a solution. The Pareto rule works in many different fields so why not to apply it here? 20% of effort can address the needs of 80% of potential users and most of them just want to get the job done as simply as possible. This approach can keep the API / functionality of the developed code lean and clear for the user and also easy for the developer to work on it further.

Ok, but we still have the 20% of the users that will need more customisation possible. How to address that?

Adding knobs

When it comes to extending the functionality of the code you develop, there are numerous approaches. All of them have pros and cons and should be weighted accordingly to an individual use-case. We will consider some of them.

Expose everything!

Let’s assume that we have only one functionality that we want to present to the user. Let’s call it void doSomething(). If the users would like to alter the behaviour of the method by adding a switch to perform the operation quickly, the API could look like this:

interface YourApi {
    void doSomething();
    void doSomething(boolean quickly);
}

Most of the users would still use .doSomething() as it suits their needs and they don’t have to know what exactly the quick switch does. Still, there can be another option to alter how the method mightbe called. It can also be done by adding a parameter int repeatCount. Most users will still use the version without parameters and some of them will just use one or the other parameter. So now we would have:

interface YourApi {
    void doSomething();
    void doSomething(boolean quickly);
    void doSomething(int repeatCount);
    void doSomething(boolean quickly, int repeatCount);
}

But you can imagine that if the next parameter is needed, the method overload count will increase rapidly. This can be still a valid approach as some well known libraries use APIs like this (i.e. RxJava Observable API). Although it is efficient to use once someone is familiar with it, it is pretty difficult to learn.

Telescopic constructor pattern

Sometimes you as the developer know that some API can get more defined only in one—linear—way. This is sometimes used in Java object constructor pattern, which looks like this:

class YourClass {
    YourClass() {
        this(false);
    }
    YourClass(boolean preciseParameter) {
        this(preciseParameter, 0);
    }
    YourClass(boolean preciseParameter, int morePreciseParameter) {
        this(preciseParameter, morePreciseParameter, null);
    }
    YourClass(boolean preciseParameter, int morePreciseParameter, @Nullable Object evenMorePreciseParameter) {
        // the only one to support
    }
}

This may leave you with just one API to support as the rest of the calls are using it. Unfortunately, if another parameter appears and needs to be added, this situation will get very similar to the ‘expose everything’ approach.

Strategy builder pattern

What seems to be the best fit for our Bluetooth libraries so far is the Strategy builder pattern.

interface StrategyBuilderApi {
    void doSomething(int repeatCount); // the most simple version of the API
    void doSomething(int repeatCount, Strategy strategy);
}

The Strategy will contain all of the possible switches that can be used for a particular call and the Strategy.Builder will allow for easy extension.

class Strategy {
    final boolean firstParameter;
    final int secondParameter;
    final Object thirdParameter;

    private Strategy(boolean firstParameter, int secondParameter, Object thirdParameter) {
        this.firstParameter = firstParameter;
        this.secondParameter = secondParameter;
        this.thirdParameter = thirdParameter;
    }

    static class Builder {
        boolean firstParameter = false;
        int secondParameter = 0;
        Object thirdParameter = null;

        /**
        * The description for the first parameter
        */
        void setFirstParameter(boolean firstParameter) {
            this.firstParameter = firstParameter;
        }

        /**
        * The description for the second parameter
        */
        void setSecondParameter(int secondParameter) {
            this.secondParameter = secondParameter;
        }

        /**
        * The description for the third parameter
        */
        void setThirdParameter(Object thirdParameter) {
            this.thirdParameter = thirdParameter;
        }
    }
}

Using this approach for adding more knobs to the API does not create a need for expanding the surface of the API. It also eases the strain for the new users who can learn how to alter the behaviour of the API by reading the Javadoc descriptions of individual parameters one by one. There are more possibilities of course, so definitely devote some time to choose the best strategy.

share


DarekSenior Software Engineer

LEARN MORE

Contact us if you have any questions regarding the article or just want to chat about technology, our services, job offers and more!

POLIDEA NEWSLETTER

Sign in and expect sharp insights, recommendations, ebooks and fascinating project stories delivered to your inbox

The controller of the personal data that you are about to provide in the above form will be Polidea sp. z o.o. with its registered office in Warsaw at ul. Przeskok 2, 00-032 Warsaw, KRS number: 0000330954, tel.: [0048795536436], email: [hello@polidea.com] (“Polidea”). We will process your personal data based on our legitimate interest and/or your consent. Providing your personal data is not obligatory, but necessary for Polidea to respond to you in relation to your question and/or request. If you gave us consent to call you on the telephone, you may revoke the consent at any time by contacting Polidea via telephone or email. You can find detailed information about the processing of your personal data in relation to the above contact form, including your rights relating to the processing, HERE.

Data controller:

The controller of your personal data is Polidea sp. z o.o. with its registered office in Warsaw at ul. Przeskok 2, 00-032 Warsaw, KRS number: 0000330954, tel.: [0048795536436], email: [hello@polidea.com] (“Polidea”)

Purpose and legal bases for processing:

 

Used abbreviations:

GDPR – Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016
on the protection of natural persons with regard to the processing of personal data and on the free movement
of such data, and repealing Directive 95/46/EC (General Data Protection Regulation)

ARES – Polish Act on Rendering Electronic Services dated 18 July 2002

TL – Polish Telecommunications Law dated 16 July 2004

1)        sending to the given email address a newsletter including information on Polidea’s new projects, products, services, organised events and/or general insights from the mobile app business world |art. 6.1 a) GDPR, art. 10.2 ARES and art. 172.1 TL (upon your consent)

Personal data:name, email address

2)       statistical, analytical and reporting purposes |art. 6. 1 f) GDPR (based on legitimate interests pursued by Polidea, consisting in analysing the way our services are used and adjusting them to our clients’ needs, as well as developing new services)

Personal data:name, email address

Withdrawal of consent:

You may withdraw your consent to process your personal data at any time.

Withdrawal of the consent is possible solely in the scope of processing performed based on the consent. Polidea is authorised to process your personal data after you withdraw your consent if it has another legal basis for the processing, for the purposes covered by that legal basis.

Categories of recipients:

Your personal data may be shared with:

1)       authorised employees and/or contractors of Polidea

2)       persons or entities providing particular services to Polidea (accounting, legal, IT, marketing and advertising services) – in the scope required for those persons or entities to provide those services to Polidea

 

Retention period:

1)       For the purpose of sending newsletter to the given email address – for as long as the relevant consent is not withdrawn

2)       For statistical, analytical and reporting purposes – for as long as the relevant consent is not withdrawn

Your rights:

 

Used abbreviation:

GDPR – Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016
on the protection of natural persons with regard to the processing of personal data and on the free movement
of such data, and repealing Directive 95/46/EC (General Data Protection Regulation)

According to GDPR, you have the following rights relating to the processing of your personal data, exercised by contacting Polidea via [e-mail, phone].

1)       to access to your personal data (art. 15 GDPR) by requesting sharing and/or sending a copy of all your personal data processed by Polidea

2)       to request rectification of inaccurate personal data
(art. 16 GDPR) by indicating the data requiring rectification

3)       to request erasure of your persona data (art. 17 GDPR); Polidea has the rights to refuse erasing the personal data in specific circumstances provided by law

4)       to request restriction of processing of your personal data (art. 18 GDPR) by indicating the data which should be restricted

5)       to move your personal data (art. 20 GDPR) by requesting preparation and transfer by Polidea of the personal data that you provided to Polidea to you or another controller in a structured, commonly used machine-readable format

6)       to object to processing your personal data conducted based on art. 6.1 e) or f) GDPR, on grounds relating to your particular situation (art. 21 GDPR)

7)       to lodge a complaint with a supervisory authority,
in particular in the EU member state of your habitual residence, place of work or place of the alleged infringement if you consider that the processing
of personal data relating to you infringes the GDPR
(art. 77.1 GDPR)

No obligation to provide data:

Providing your personal data is not obligatory, but necessary for Polidea to provide you the newsletter service

Refusal to provide the above data will result in inability to receive the newsletter service.

Profiling

In the process of providing the newsletter service, we make decisions in an automated way, including profiling, based on the data you provide.

 

“Profiling” means automated processing of personal data consisting of the use of your personal data to evaluate certain personal aspects relating to you, in particular to analyze or predict aspects concerning your personal preferences and interests.

 

The automated decisions are taken based on the analysis of clicked and viewed content. They affect the targeting of specific newsletter content to selected users registered to receive the newsletter service, based on the anticipated interests of the recipient.