Cloud-Native Applications and Managing Their Dependencies

Cloud-Native Applications and Managing Their Dependencies

Many businesses are transitioning their operations to the cloud because it enables them to be more agile, respond faster to market changes, and scale their businesses more easily. However, moving to the cloud can also be a daunting task because it requires significant planning and execution. The term “cloud-native” refers to a methodology for developing and operating applications that makes use of the benefits offered by the cloud computing delivery paradigm.

What Does it Mean to be “Cloud-Native”?

Where applications are hosted is not important. What is important is how they are developed and deployed. Cloud-native development can be used with either public or private clouds. Any cloud storage is able to store and provide access from any of the public gateways in the world, regardless of where they are physically located. They are able to provide on-demand access to processing power as well as up-to-date data and application services for software developers. Cloud-native applications are developed using microservices, which are small, independent services that work together to form a larger application. 

Microservices are a type of software architecture that allows developers to build and maintain large applications more easily. In a microservices architecture, an application is divided into small, independent parts called services. Each service can be developed and deployed independently, making the overall process more flexible and scalable.

Microservices can benefit the cloud infrastructure in several ways. First, they allow for more granular control over individual parts of an application. This makes it easier to deploy and manage applications in the cloud. Secondly, microservices make it easier to scale applications. When an application needs to be scaled up or down, only the services that need to be changed need to be updated. Finally, microservices can improve availability by allowing for rolling updates and deployments. If one service goes down, the others can continue to run, making the overall system more resilient.

When a data request is made, it is routed through a number of distinct Docker containers, each of which is running a separate set of microservices to provide service to the consumers. They are created with the intention of delivering commercial value that is widely acknowledged, such as the capacity to rapidly incorporate user feedback to achieve continuous improvement. Each container is responsible for the operation of a single service that is directed toward serving the customers. These containers are able to offer users scalability and the appropriate level of protection.

How Do Dependencies Fit In? 

A dependency is an implicit or explicit relationship between one piece of code and another. It can be thought of as a requirement that one piece of code has for another piece of code in order to function correctly.

There are two main types of dependencies: hard dependencies and soft dependencies. Hard dependencies are dependencies that cannot be changed without breaking the code that depends on them. Soft dependencies, on the other hand, can be changed without breaking the code that depends on them.

Dependencies can be either internal or external. Internal dependencies are dependencies between two pieces of code within the same software system. External dependencies are dependencies between two pieces of code that reside in different software systems.

In a cloud-native application, each microservice has its own dependencies. These dependencies are managed by the container in which the microservice is running. The container is responsible for ensuring that the correct versions of the dependencies are used and that they are kept up-to-date.

Since the development of those features from scratch would need a significant amount of time and because of the complexity of their design, it is much more efficient to use existing solutions. Because so many dependencies are required, solutions to manage them are also required and hence we have package managers such as Maven or NPM. 

NPM, for example, calls for a wide variety of dependencies to be loaded into the container before it can be deployed. Due to the fact that many dependencies are open source, a variety of researchers have access to and uncover vulnerabilities in them, which is one of the many reasons why they receive updates.

Dependencies are of large concern to developers because, if neglected, they can become a security issue. If a developer is not careful, they can easily introduce vulnerabilities into their codebase by depending on code that has known vulnerabilities. This is why it’s important to scan third-party dependencies before installing them and apply security patches when they’re available. 

For example, if we are talking about NodeJS, it typically gets updated once a month, and each of those updates fixes a couple of vulnerabilities. Thus, it is essential to update those systems on a regular basis to ensure that we can avoid as many dependency-related vulnerabilities as possible.

Best Practices for Dependency Management

When we talk about dependency management, we talk about a lot of different strategies and things that are being considered, such as using an automated dependency management tool or a package manager. Nonetheless, to ensure that the dependencies are effectively managed, the following are a few of the best practices that can be utilized.

Detecting All the Unused Dependencies

You can use the depcheck to check whether or not there are any dependencies that are not being used. The following command needs to be used to install the depcheck.

npm install depcheck -g

Once installed, you can run the following command to check for unused dependencies.

depcheck

Detecting All the Outdated Dependencies

Most dependencies are open source and usually get updated once in a while as and when security researchers find vulnerabilities or a new functionality is added. Hence, it is possible that your dependencies get outdated. Thus, it is essential to verify and update outdated dependencies

To check for outdated dependencies, you can just open the terminal by navigating to your NPM folder and run the following command:

You can also use a simple dependency checking script. It will check all the dependencies of a repo or package:

#!/bin/bash
DIRNAME=${1:-.}
cd $DIRNAME

FILES=$(mktemp)
PACKAGES=$(mktemp)

find . \
    -path ./node_modules -prune -or \
    -path ./build -prune -or \
    \( -name "*.ts" -or -name "*.js" -or -name "*.json" \) -print > $FILES

function check {
    cat package.json \
        | jq "{} + .$1 | keys" \
        | sed -n 's/.*"\(.*\)".*/\1/p' > $PACKAGES

    echo "--------------------------"
    echo "Checking $1..."
    while read PACKAGE
    do
        RES=$(cat $FILES | xargs -I {} egrep -i "(import|require).*['\"]$PACKAGE[\"']" '{}' | wc -l)
        if [ $RES = 0 ]
        then
            echo -e "UNUSED\t\t $PACKAGE"
        else
            echo -e "USED ($RES)\t $PACKAGE"
        fi
    done < $PACKAGES
}

check "dependencies"
check "devDependencies"
check "peerDependencies"

Keeping Desired Dependencies Updated

Due to the wide variety of dependencies that are being utilized, it is necessary to ensure that the desired dependencies are kept up to date consistently to ensure the best performance. Checking and upgrading those dependencies manually typically takes a significant amount of time. Thus, a wide variety of organizations utilize automated dependency management tools to ensure their dependencies are kept up to date on a consistent basis and in a timely manner. The dependencies in the NPM application are defined in the package.json file of the repo. The files have this type of content:

{
  "name": "herodevs-packages",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "precommit": "lint-staged",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "build-lazy": "ng build lazy",
    "build-dynamic": "ng build dynamicService",
    "npm-pack-lazy": "cd dist/loader && npm pack",
    "npm-pack-dynamic": "cd dist/dynamic && npm pack",
    "package-lazy": "npm run build-lazy && npm run npm-pack-lazy",
    "package-dynamic": "npm run build-dynamic && npm run npm-pack-dynamic",
    "package": "rm -rf dist/ && npm run package-dynamic && npm run package-lazy"
  },
  "private": false,
  "dependencies": {
    "@angular/animations": "^8.0.0",
    "@angular/common": "^8.0.0",
    "@angular/compiler": "^8.0.0",
    "@angular/core": "^8.0.0",
    "@angular/forms": "^8.0.0",
    "@angular/platform-browser": "^8.0.0",
    "@angular/platform-browser-dynamic": "^8.0.0",
    "@angular/router": "^8.0.0",
    "core-js": "^2.5.4",
    "rxjs": "~6.5.2",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.800.0",
    "@angular-devkit/build-ng-packagr": "~0.800.0",
    "@angular/cli": "~8.0.2",
    "@angular/compiler-cli": "^8.0.0",
    "@angular/language-service": "^8.0.0",
    "@types/jasmine": "~2.8.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~8.9.4",
    "codelyzer": "^5.0.1",
    "husky": "1.3.1",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~3.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~1.1.2",
    "karma-jasmine-html-reporter": "^0.2.2",
    "lint-staged": "8.1.0",
    "ng-packagr": "^5.1.0",
    "prettier": "1.16.1",
    "protractor": "~5.4.0",
    "ts-node": "~7.0.0",
    "tsickle": "^0.35.0",
    "tslib": "^1.9.0",
    "tslint": "~5.11.0",
    "typescript": "~3.4.5"
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "prettier --parser typescript --writeprettier --parser typescript --write",
      "git add"
    ]
  }
}
Footer
© 2022 GitHub, Inc.
Footer navigation
Terms
Privacy
Security

Source

Using an Automated Dependency Management Tool

Automating dependency management can be helpful in a few ways. Not only can it speed up your development process, but it can also ensure that everyone on your team is using the same versions of dependencies. Automation tools work by looking at the dependencies you have declared in your code and comparing them to the versions that are available. If there is a newer version available, the tool will update your project to use it.

The changelog associated with the dependency is typically included in the pull request. You have a lot of different options to choose from when configuring the dependency management tool, such as the update time, which dependency has to be updated, what conditions need to be met if the pull request needs to be merged automatically, and many other things. 

One example is Mend Renovate, which is an open-source tool that automatically creates pull requests for all types of dependency updates. Renovate is different from other dependency update tools because it is completely configurable and can be set up to automatically update dependencies on a regular basis, or only when there are new security updates. It offers features such as fully automated pull request creation and merging, dependency selection based on package popularity and testing data, support for multiple package managers including npm, yarn, composer, and customizable update rules for each repository.

Conclusion

In a cloud-native world, a typical environment is supported by a wide range of dependencies. Thoroughly testing these dependencies is critical to the success of any cloud-native application. However, it can be difficult and time-consuming to manually update all the dependencies. Automated dependency management tools can help to reduce the amount of time spent on managing dependencies and can also improve the quality of your code. In this post, we covered the best practices for managing dependencies in cloud-native applications.

Guy Bar-Gil / About Author

Guy Bar-Gil is an experienced Head of Product-Led Growth and leads product-led growth at Mend. He loves engaging with people to understand and solve complex problems, with a special passion for product and company strategy. Prior to joining Mend, Guy held positions in R&D teams and served as a combat operator in the IDF.

LinkedIn