SDK Development Guidelines
Preamble
These are common guidelines for Rakuten teams building Android and/or iOS SDKs/libraries.
Contribution
We encourage everyone to create PR contributions for additional guidelines, or create issues for any discussion points.
Terminology:
- The term “library”, “SDK”, “module” and “SDK module” are used interchangeably in this document.
- MAY, SHOULD, SHOULD NOT, MUST, MUST NOT, RECOMMENDED are defined in RFC 2119.
- We distinguish between production code, i.e. code that is exported/visible to clients, and supporting code, i.e. code that is not exported/visible to clients, e.g. tests, build scripts, utilities.
- We distinguish between exported dependencies, i.e. dependencies that consumers of our modules will automatically get in their projects as well, and not exported dependencies, i.e. dependencies that are completely hidden from SDK consumers.
Guidelines
- Project Files & Source Control Management
- Build
- Programming Language
- Coding Style
- Library Design
- Testing
- Documentation
- Versioning
- Publishing
- Continuous Integration & Deployment
Project files & Source Control Management
- MUST be under source control management (SCM). RECOMMENDED source control management is git
- If you intend your source repo to be public ensure you have received the necessary management and legal approval
- MUST use a .gitignore configuration
- Source code repositories SHOULD follow the naming convention
[platform]-[library name]
, e.g.android-analytics
,java-datastore
,ios-inapmessaging
,swift-utils
- Rationale: encoding version information (e.g. alpha/core/stable) in the repository breaks links and integration when a module changes
- Exceptions: existing modules that already carry additional information in the repository name & renaming would break too many existing integrations.
- MUST NOT restrict read access to source code repositories unnecessarily
- For internally hosted code → add the appropriate mailing list with read access
- For open source projects → public repo on GitHub
- RECOMMEND to not have source file header comment blocks (such as the Xcode/Java default header comment) as these easily get out of date and the information such as author/version of commits is tracked in SCM
- For Android:
- SHOULD follow the standard project layout for Android or for Java projects respectively
- MUST prefix all exported resources (layout files, xml files, drawable files, color ids, dimension ids, string ids, etc.) with
+ "_" (e.g. "sug_" for a Suggestion Module) using [resourcePrefix](https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.LibraryExtension.html#com.android.build.gradle.LibraryExtension:resourcePrefix) in the gradle build
- For iOS:
- SHOULD follow the standard project layout for iOS projects.
Build
- Development version MUST build with single command after clean clone, i.e. no local setup necessary
- MUST NOT depend on locally stored libraries
- MUST NOT use wildcards (+) for exported dependencies, i.e. dependencies that SDK clients will inherit
- MUST have a sample app - a minimal app that integrates the SDK and shows how to use the SDK features. It SHOULD be written in Kotlin (Android) or Swift (iOS).
- For Android:
- MUST use gradle with gradle wrapper to build Android library or Java library
- MUST have consistent minSdk and supportLib versions with other SDKs
- SHOULD use shared build configuration for consistent version with other SDKs/libraries
- MAY use manual configuration with values (same as in shared build configuration), if so
- SHOULD use a recent minSdk version unless there is an engineering or product decision to support older versions
- SHOULD use latest versions of Android X libraries (if required)
- Example 1: using the auto value annotation processor in version 1.4-rc1 is allowed because it is not exported to SDK clients
- Example 2: using okhttp compile/implementatino dependency in version 3.4.0-RC1 is not allowed because that dependency will be imposed onto SDK clients
- For iOS:
- SHOULD be built from a CocoaPods Podspec or Swift Package Manager (SPM)
- SHOULD use fastlane to automate tasks and MAY use our shared build configuration to help with setup and maintain consistency
- SHOULD set minimum deployment target to a recent iOS version unless there is an engineering or product decision to support older versions
- Xcode unit test project SHOULD pull the module’s source from a local Podspec path reference
- Xcode sample project MAY be separate from the unit test project and sample project SHOULD pull the module’s source from a local Podspec path reference
- Xcode project SHOULD build using the latest version of Xcode and the iOS SDK with no warnings
Programming Language
Production Code
- For Android:
- MUST use Kotlin or Java. Kotlin is RECOMMENDED
- RECOMMENDED to use Java 11 language features
- MAY NOT use other JVM languages (except for Kotlin)
- For iOS:
- MUST use Swift or Objective-C. Swift is RECOMMENDED
- If the SDK needs to execute code before the application is launched you MAY use Objective-C (e.g.
+ (void)load
) for at least that part of the code. This is because Swift does not have reliable static initializers
- If the SDK needs to execute code before the application is launched you MAY use Objective-C (e.g.
- MUST use Swift or Objective-C. Swift is RECOMMENDED
- MAY use native C/C++/other languages when necessary
Supporting Code
- For Android:
- MAY use other JVM langages for testing
- SHOULD use gradle (groovy or kotlin) for build scripts
- For iOS:
- SHOULD use Swift for test code
- SHOULD use fastlane (Ruby) for automation but MAY use other languages
- MAY use any language/framework/tool for other supporting code
Coding Style
- Code MUST follow a style guide such as:
- Kotlin’s official coding conventions
-
To configure the IntelliJ formatter according to this style guide, please install Kotlin plugin version 1.5.0 or newer, go to Settings Editor Code Style Kotlin, click on “Set from…” link in the upper right corner, and select “Predefined style / Kotlin style guide” from the menu
-
- Google Java Style
- Swift
- Objective-C
- Kotlin’s official coding conventions
- Existing modules SHOULD follow a coding style guide. Always strive for consistency within a project’s codebase
- SHOULD use static code analysis, such as detekt or SwiftLint to minimise errors and minimise debates and formatting fix-up commits in PRs. It is RECOMMENDED to integrate these tools with the CI workflow.
- SHOULD use quality tools available in common config: Android and iOS
- For Android:
- MUST use the Android Support Annotations for public APIs
- Non-Android Java code MUST use the Jetbrains Annotations for public APIs
Library Design
Limit impact on consumers
- SHOULD limit dependencies as much as possible primarily due to risk of version clashes (e.g. SHOULD NOT depend on external networking library) but also because the dependency may stop being maintained
- RECOMMENDED not to have any exported dependency on other SDK modules e.g. if your module needs authentication, do not depend on Authentication SDK. Instead, require your library consumer to pass an access token string to your API instead.
- For Android:
- MUST use a consumerProguardFiles file if obfuscation breaks the libraries function
- SHOULD NOT have unnecessary exported dependencies, in particular
- SHOULD NOT leak networking library to consumers
- SHOULD NOT leak serialization library to consumers
- An exception to this rule are optional adapters for popular libraries
Expose minimal Surface area
- Keep everything private unless it needs to be public. Every public class, method, property & resource MUST have a strong reason for being public
- MAY keep internal source code in a
/Private
folder to allow tools to more easily ignore those files - MUST hide classes from generated API documentation that are required to be public solely for technical reasons and are not intended for SDK consumers
- SHOULD declare classes as final to avoid unexpected high-jacking of SDK behavior by subclasses
- For Android:
- SHOULD use
RestrictTo
annotation on classes that are internal to the library
- SHOULD use
Best Practices
- For better testability of your library it is RECOMMENDED to use dependency injection
- Simple configuration keys MAY be settable in application’s manifest file (Android) or Info.plist (iOS) or passed directly to an API configuration function:
- For Android:
- the manifest file is read in a (fake) ContentProvider and be automatically initialized (if that fits with your library design)
- Complex Configurations MUST use the builder pattern with value objects for configuration values
- Exception: When using Kotlin named arguments and default arguments
- For iOS:
- Take the https://swift.org/documentation/api-design-guidelines/ into account when designing your public API interface
Testing
- All library (sub-)projects MUST generate coverage test reports (e.g. jacoco, Xcode/slather)
- Unit test coverage MUST be at least 80% and SHOULD be above 85%
- For Android:
- Unit tests MUST NOT rely on reflection to expose otherwise private/internal classes or members
- Tests SHOULD use JUnit, and MAY use helper libraries such as Mockito or Robolectric
- For iOS:
- Tests SHOULD use XCTest, and MAY use helper libraries such as OCMock or OHHTTPStubs
- Tests MAY be BDD style using Kiwi for Obj-C or Quick/Nimble for Swift
Documentation
- Public API MUST be annotated with a standard documenting style e.g. Appledoc, Javadoc or KDoc
- Exception: when intent is obvious
- Exception: when class is excluded from document generation
- Libraries MUST publish a self contained user guide
- Libraries MUST publish a changelog with dates of releases. If you can’t link to an issue tracker due to privacy/access rights you should include a full description of the change.
- Libraries MUST contain developer documentation with the source code
- Both user guide and developer documentation SHOULD render readable in the SCM repository web interface (Bitbucket Server/GitHub)
- Versioned User Documentation (e.g. docs + user guide) MUST be published and SHOULD use a script to automate publishing
- Internal libs → internal GitHub pages
- Open Source libs → public GitHub pages
Versioning
- MUST follow Semantic Versioning
- MUST tag source control management revision/commit that is published
- RECOMMENDED For Android: Use gradle-versions-plugin for automated versioning by git tag
- We consider 0.x versions alpha and 1.0 and above stable
Publishing
- Version MUST follow Versioning guide above
- Libraries SHOULD publish sources internally or to GitHub if open sourced
- For Android:
- Library MUST publish binary as aar or jar to a maven or ivy repository.
- Internal libraries MUST be published in the Mobile Libs Artifactory Repo
- Public libraries SHOULD be published in Maven Central
- For iOS:
- Library SHOULD publish Podspec or Swift Package Manager (SPM) manifest
- Internal libraries that use CocoaPods SHOULD publish their Podspec in the MAG spec repo (fork and create PR)
- Public libraries that use CocoaPods SHOULD be published to CocoaPods master repo
- Libraries MAY publish a Podspec that points to a framework binary and keep their source private
Continuous Integration & Deployment
- MUST use scm branching + code reviews + continious integrations to ensure quality (e.g. run all tests for all code changes)
- PRs MUST be approved after code review by at least 1 code owner before merging
- PRs MUST run all automated tests without failure before merging
- PRs MUST address all tasks/comments (created by reviewers) before merging
- PRs SHOULD run all quality checks
- SHOULD integrate CI with code review system
- SHOULD use automated deployments