Saturday, 29 April 2023

SDKs

What are the SDKs? 

SDKs refer to the software development kit. It is generally ready-to-use code published as a package that you can install and use with a tiny effort.

There are many use cases for that, for example, when you implement a UI unit that the company will use in many projects it will be faster to push it in a package so everyone on the other projects doesn't need to implement or copy-paste it. Another example is when a company provides service for other companies it will be much faster to publish an SDK that contains all functions provided by that company rather than talk to each company, send long documents with DTOs, APIs, endpoints ...etc. and have meetings and assist each new company.

There are other package types, for example, the framework you code with is just a group of packages, or even the applications you use on your devices, but for simplicity let's talk only about the SDK.

Why? Many of the programming problems you face today someone faced yesterday. To prevent things like duplicating code on many projects, the headache of updating all code copies and to speed up the integration process between different software components.

Package versioning 

All packages contain a header to define them, the header contains useful info like who publishes it, the targeted framework ...etc. but the most important thing is the package name so you can install, define and use the package in your project, and the second thing is the package version number.

The package version number is the tricky one, you will need to understand it deeply (will know why in the dependency resolution section). As there are always new things in the software development world, any package has to keep updated with these changes. This will result in having different versions of the packages so what is the problem? Let's just give it an auto-increment id so the users know the latest version. Actually, having single digits will make the difference between versions too ambiguous, there is no guarantee that each version has compatible issues or not, is it just a small fix or a major change that we need to upgrade the framework and check our package interface. To solve these problems let's take a look at Semantic Versioning.

Semantic Versioning (SemVer)

It's a standard versioning system to define the package version, firstly it states that the package number contains three elements separated with "." in the following format: MAJOR.MINOR.PATCH, Here you can find the definition of the three elements:

  1. MAJOR version when you make incompatible API changes
  2. MINOR version when you add functionality in a backward-compatible manner (will explain later)
  3. PATCH version when you make backward-compatible bug fixes

There are many rules but the ones you will need to be aware of them:
  1. Each element contains only natural numbers without leading zeroes. 
  2. Each element MUST increase numerically. You must set the elements on their right with zeroes when increasing an element. (1.11.23 -> 2.0.0 and 2.4.23 -> 2.5.0)
  3. The contents of that version can't be modified after the versioned package has been released.
  4. A pre-release version is defined only by stating that on the rightest part using only ASCII characters. (1.0.0beta)

The incompatible changes

Sometimes you will upgrade the package version to add logs for example, these changes will have zero effect on code usage as the outer packages will not care about that implementation as long as everything runs smoothly, whatever you use the older or the newer version the outer package will not have any compatible issues.

But other times you will need to change something more critical something like an interface, or model data, in these times if the project uses an older version it will crash on the runtime saying for example (I want an interface with three functions but the package hasn't it).

So, these types of changes are stated as Major and if the project has a package with 2 major versions it will just tell you to resolve this issue before running the project and that is exactly why Dependency resolution.

Dependency resolution

The former definition of it is the process of finding and installing the correct version of a package and its dependencies. before diving deep let's show a complex state that will highlight the problem 

Normally packages can use other packages. but there is no guarantee that all packages will use the latest package version. Imagine you publish package A and then package B, and package B use package A with version 1.0.0, lately, you needed to update package A to have more features to use in package C, and package C use package B at the same time, the question here what will be package A version?

You can check the following diagram for another example:

Most of the time the package version graph contains these samples:

  • ">=x" means Developer doesn't care which version you use as long as it is greater than x and less than the next major change, generally speaking: the package manager gives you a warning for incompatible versions.
  • "=x" means you can only use version x else you need to update the package even for tiny changes. This style has limited use cases; generally, don't use it unless you need to.
  • "=x.*" means use the latest stable version with the prefix package number x. This option will remove the headache of manually upgrading all other packages and repo each time you release a minor or patch change, this also can be customized to accept only patch changes.

There are some common rules here:

Lowest applicable version: that state that the project will use only the version that fits with the highest package version asked without upgrading to the latest unless using float point 

For example, if packages ask for package X with versions (1.0.0, 1.2.10, and 1.2.1) the project will select version 1.2.10 in the whole project even if package X has a newer version (for example 1.3.0).

Direct dependency wins: When the package graph for an application contains different versions of a package in the same subgraph and one of those versions is a direct dependency in that subgraph, that version would be chosen for that subgraph and the rest will be ignored. 

Sometimes this will result in forcing an update, for example, if you use package Y that uses package X with version 2.0.0 but you told the project to build with package X =3.0.0 or =1.0.0 the project will ask you to solve to that version 2.0.0 or go to the package Y and upgrade package X.

Other rules depend on your use case and the framework and I want to keep it as simple as possible.

Resource:

Semantic Versioning 2.0.0 | Semantic Versioning (semver.org)

NuGet Package Dependency Resolution | Microsoft Learn

No comments:

Post a Comment

Database Decisions: Choosing Between Relational, Document, and Graph Models for Your System

Choosing the right database is one of the most critical decisions in system architecture. Whether you're dealing with structured or unst...