Pioneering Future Technologies Today
At Nirman Labs, we blend innovation with reliable practical solutions to propel your business into the future
Explore More
"Trusted by companies from diverse verticals worldwide"
Empowering businesses with the cutting-edge technology needed to thrive in a dynamic digital era.
Distributed Tracing & Instrumentation
In today’s IT World, It is very essential to maintain the health and performance of applications and their operations. Distributed Tracing and its Instrumentation are the same for maintaining and optimizing the performance, reliability, and scalability of IT infrastructure, especially in reference to complex distributed systems. Distributed Tracing gives an insight into the performance and information about the application’s connection across multiple systems and services. Tracing is useful for identifying the bottlenecks, dependencies, and errors by tracking application requests when they travel through a distributed system's different components. This detailed view of a system and its services is very useful in complicated situations where services are distributed across multiple physical and virtual machines. This blog explores the fundamentals of Distributed Tracing and Instrumentation, how they work, and best practices for implementing them successfully using different tools. ### Introduction To Distributed Tracing Distributed Tracing is a way for monitoring services and troubleshooting issues in microservices systems. Sometimes applications become more distributed in the context of microservice architecture, and finding the root cause of errors or performance bottlenecks becomes more difficult. Distributed Tracing helps in this by allowing you to track requests as they travel through multiple services of the application. Distributed Tracing is also useful for identifying latency issues, understanding service dependencies, and improving system reliability. ### How Distributed Tracing Works There are two main components in the concept of Distributed Tracing - Traces and Spans. - **Traces:** It represents the complete journey of a single request when it travels through the various services of a distributed system. It provides a comprehensive view of all the operations performed in response to the request. - **Spans:** Each trace is made of multiple spans, where each span represents a specific unit of work or an operation within a single service. Spans provide critical metadata including start and end times, operation names, and other service-specific information. They can also be nested; spans can contain sub-spans that provide more detailed visibility into the operations performed by a service. Spans and traces are identified and linked through a unique ID, which allows them to be analyzed in a comprehensive way that shows how different parts of the system interact throughout a request's lifetime.  ### Different Tools For Distributed Tracing Here we have some tools for Distributed Tracing that can be integrated smoothly with existing systems and can provide comprehensive insights into application performance: - **Jaeger:** Developed by Uber, Jaeger is an open-source tool for monitoring and troubleshooting transactions in complex distributed systems. It has capabilities like real-time trace search and visualization, root cause investigation, and performance optimization. - **Zipkin:** Developed by Twitter, Zipkin is another open-source solution for capturing timing data which is required for solving latency issues in service architectures. It features a simple web UI where traces can be analyzed - **OpenTelemetry:** An observability framework for cloud-native applications, provides APIs, libraries, agents, and Instrumentation to help developers collect and export telemetry data (traces, metrics, and logs). It aims to provide a unified set of APIs and libraries that can be used with multiple backend systems. - **Dynatrace:** A commercial product that provides automated, high-fidelity performance monitoring. Dynatrace uses artificial intelligence to detect performance issues and automate root-cause analysis. It supports full-stack monitoring, from applications to the underlying infrastructure. - **Datadog:** A monitoring service for cloud-scale applications, Datadog provides observability into your applications through tracing, log management, and real-time performance monitoring. It works easily with most cloud providers and supports various programming languages. These tools typically integrate with existing systems through Instrumentation. Developers add libraries or agents to their code or infrastructure, which then collect and send trace data automatically to a central system for analysis. There isn’t too much modification in the current codebase for the Instrumentation. ### How To Do Instrumentation: Instrumentation is the act of adding observability code into the app. Let's understand the process of Instrumentation with the sample example of Instrumenting an application using OpenTelemetry in Go. **Step:1 Install Necessary Packages** Begin by installing the required OpenTelemetry Go packages. You will need the SDK to produce telemetry and the API to instrument your code. **Step:2 Set Up the Exporter** For sending your telemetry data to a tracing backend (such as Zipkin, Jaeger, or an OTLP collector), you need to set up an exporter. This involves adding a function or method in your application that initializes your chosen exporter. **Step:3 Initialize Tracer Provider** A tracer provider manages the creation of tracers. It involves integration of the SDK with your exporter and setting resource attributes that help identify your application uniquely across different services. **Step:4 Acquire a Tracer** Once your tracer provider is Initialized, you can use a tracer. A tracer creates spans. Spans represent individual units of work and functionality within your application. **Step:5 Create Spans** Use the tracer to create spans for the process you want to trace. This involves the creation and completion of a span that covers the whole function or a block of code. Each span can record timings, operations, and additional metadata. **Step:6 Propagate Context** Ensure that the context, which contains the tracing information, is propagated correctly throughout your application, especially when making requests to external services which often involves passing a context.  **Step:7 Monitor and Adjust** Once your application is instrumented and running, use the obtained traces to monitor your application's performance and behaviour. Adjust your Instrumentation as needed to focus on important tasks or to capture additional details for debugging complex problems. By following these basic steps, you can effectively instrument your Go application to having insights of service’s functionality and identify performance bottlenecks and issues. As this is a manual Instrumentation, It gives you the flexibility to customize the level of details and scope of the tracing to match your specific requirements according to service architecture. ### Challenges And Considerations Although Distributed Tracing is powerful, There are many challenges such as data overloading, privacy concerns, and sometimes high cost of Instrumentation maintenance. There should also be some considerations like performance and data security concerns that require a thoughtful approach. It is also very important to choose the right tools for Tracing and Instrumentation. ### At Last, Distributed Tracing improves system monitoring and performance by providing the details that are required for understanding and optimizing complex distributed workflow. The use of Distributed Tracing makes it easier to optimize microservices by providing accurate and useful insights into the microservice interactions. We can have more effective resource utilization by adopting Distributed Tracing. Many companies have adapted the is technology and are currently equipped to handle large-scale operations more effectively, respond more swiftly to dynamic changes, and have gallops of customer satisfaction. As of these many significant benefits, organizations should evaluate and enhance their existing tracing practices and should adapt if not have any. Proper improvements in Distributed Tracing can lead to deeper and more precise operational insights with better decision-making and can have a significant competitive edge in the digital marketplace.
Introduction to Test Cases
In the software testing process after requirement analysis and test plan, we have a Test design phase. In this phase, we need to write the test cases from different test scenarios as per the requirement of an application. Test cases are instructions for testers to follow to ensure programs are functioning properly. Test case writing converts user requirements into a set of test conditions and descriptions that indicate how a system is functioning. Applications must be tested through these test cases to find out how the system behaves under all possible conditions. A clear understanding of the application and the testing process can make writing test cases that identify defects easier. While writing such test cases we need to follow some factors that make it perfect for all the perspectives. Here are some factors to get through the process. **1. Effectiveness** Writing test cases in an effective manner is a very important thing. Many people think that writing too many test cases covers all the use cases. But in actuality, it increases the load of the resource and also wastes too much time. So we need to think effectively while writing test cases so that it provides maximum coverage with fewer test cases. It could be further improved by dividing them into subsets like there is such test cases be common for some test scenarios. **2. Simplicity** Test cases should be written in simple words. No complex things should be included regarding the inputs and outputs of the test case. For that everyone involved in the test case writing process should use the same format for designing them. We should aim to write them in a way that even a non-technical person can also understand the purpose of a test case. **3. Understanding** Before writing a test case we should have a proper understanding of a requirement. For example, there could be many common things in each application that are sure to bring out errors. For that, we did not need to handle it specially. **4. Reusability** Test cases should be written in a way that they become reusable units for the future for creating new test cases. Testers can refer to the previous test cases to create a new one which makes it easier. So while creating a test case we should also keep in mind that it might be reused in a different scenario, a different but similar project. It saves time and efforts of resources. **5. Regression testing** When new features are added old test cases should be rerun to make sure nothing tested has affected and outputs are the same as expected. These regressive test cases are part of the test case policy. When test cases are re-run, some may give unexpected results which may not always be because of new changes in the code but rather a test case that is inappropriate under new criteria. Therefore, having regressive testing in mind it would help rewrite a test case when changes are made. **6. Self review** After test cases are written, review them from the tester's perspective and verify that the test case appropriately addresses the scenario and would be easy to execute. Make sure the test case is understandable to everyone and the results are appropriate. **7. End-user perspective** Software testing provides user quality software. Errors that are found through test cases are removed during the testing process. When writing a test case, think of the end user and how the user experience would be affected should the test case fail to cover a scenario from the end user's perspective. **8. Not-feature** Most test cases are written to test the features offered by an application. However, it is also necessary to test for not-features, i.e. those abilities of the software that should not exist and may be used to exploit the application. These kinds of test cases are part of exploratory testing. **9. Management** The use of proper management tools and systems is important for test cases because the count of test cases increases very fast. So it is important to use a proper management tool from the beginning. We can use simple spreadsheets or specialised tools which are available in the market as per our budget to manage test cases more effectively. **10. Peer review** If you review the test cases which are written by yourself then no matter how many times you go through it. It is possible that you will not find anything. For this, peers should review the test cases from the others or developers for the better result. After the review is done by others, the tester should make the changes that are suggested. It makes our test cases more reliable and consistent. **11. Backward Approach** Initially, when we write a test case we have expected the result or the output, and we write in that aspect to get that output. But in the backward approach instead of thinking what the result would be for a certain input, we can think of what inputs could be for a certain output. In this way, we can try with invalid inputs to get the same output.
Promises, Async/Await: For Async Code Execution
Promises help us to manage the flow of code execution when working with tasks that might take some time to complete or not completed yet but will be completed in future either with success or failure such as making network requests, reading files. Promises provide a way to handle asynchronous operations and their end result. Promises have three States - **Pending** - **Fulfilled** - **Rejected** Let’s understand promises with example of withdrawing money from ATM Suppose you're going to withdraw money from an ATM Machine 🏧. You insert essential details for withdrawal like amount pin etc. This process sends a request to the bank's server to check your account balance and this process will take some time. If a sufficient amount is available for withdrawal then it will resolve your request and deduct the amount from your account and dispense cash. If amount is not sufficient then it will reject your request and sends error message **“Insufficient Amount”** ``` function withdrawingFromATM(amount) { const balance = 1000; return new Promise((resolve, reject) => { setTimeout(() => { if (amount <= balance) { const withdrawalAmount = balance - amount; resolve(`withdraw amount ${withdrawalAmount}`); } else { reject(`Insufficient funds!!!`); } }, 2000); }); } withdrawingFromATM(5000) .then((message) => { console.log(message); }) .catch((error) => { console.error(error); }); ```  ## Promise Methods **Promise.all** Promise.all() is a function that takes an array of promises as input and returns a single promise.This promise is resolved If all the promises given to it are resolved, if any one of them is rejected then promise.all will be rejected. **Promise.any** Promise.any() is a method that takes an array of promises as input and returns a single promise. This single promise will fulfil if any of the input promise is fulfilled or it rejects if all the input promise is failed. **Promise.allSettled** Promise.allSettled() is a method that takes an array of promises as input and returns a single promise. This single promise will fulfil if all of the input promise is fulfilled with array of objects which defines end result of each promise ``` // Let’s Consider a example of withdrawal money from multiple accounts const promisesArray = [ printAccountStatement(""), printAccountStatement("account2"), printAccountStatement("account") ]; function printAccountStatement(account) { return new Promise((resolve, reject) => { setTimeout(() => { if (account && account.length > 0) { resolve(`${account}'s monthly statement`); } else { reject(`Invalid account Number`) } }, 500); }); } // Promise.any Example Promise.any(promisesArray).then(results => { console.log(`\nresult ${results}`) }) .catch(error => { console.log(`Error: ${error}`) }); // Promise.all Example Promise.all(promisesArray) .then(results => { results.forEach(result => { console.log(`\nresult ${result}`) }) }) .catch(error => { console.log(`Error: ${error}`) }); // Promise.allSettled Example Promise.allSettled(promisesArray) .then(results => { results.forEach(result => { console.log(`\nresult ${result.status}`) }) }) .catch(error => { console.log(`Error: ${error}`) }); ``` **Promise Chain** Promises Chain is used to handle a number of asynchronous operations that are depending on each other. In the promise chain output of the first asynchronous operation is used as the input of the second one. Let’s understand promise chain with example of ATM Machine. Suppose you are going to withdraw money from money. When you perform a withdrawal money transaction from an ATM, It performs a chain of promises to handle various operations. It will perform a sequence of steps for withdrawal. First, It will check the balance of your account. If sufficient balance is available for withdrawal then another promise that will deduct the amount from your account after deducting the amount of money another promise will print receipt for withdrawal operation. ``` // Let’s Consider a example of withdrawal money from multiple accounts const promisesArray = [ printAccountStatement(""), printAccountStatement("account2"), printAccountStatement("account") ]; function printAccountStatement(account) { return new Promise((resolve, reject) => { setTimeout(() => { if (account && account.length > 0) { resolve(`${account}'s monthly statement`); } else { reject(`Invalid account Number`) } }, 500); }); } // Promise.any Example Promise.any(promisesArray).then(results => { console.log(`\nresult ${results}`) }) .catch(error => { console.log(`Error: ${error}`) }); // Promise.all Example Promise.all(promisesArray) .then(results => { results.forEach(result => { console.log(`\nresult ${result}`) }) }) .catch(error => { console.log(`Error: ${error}`) }); // Promise.allSettled Example Promise.allSettled(promisesArray) .then(results => { results.forEach(result => { console.log(`\nresult ${result.status}`) }) }) .catch(error => { console.log(`Error: ${error}`) }); ``` **Async/Await** Callback hell means a situation in JavaScript where nested callbacks are used to handle asynchronous operations. This makes our code difficult to read, understand, and debug due to complexity. To resolve this async/await is used. async/await, which provide more structured and readable ways to handle asynchronous operations. An async function is a function that always returns a promise. It allows you to use the await keyword within the function. The await keyword is used to pause the execution of an async function until the promise is resolved or rejected. we can use try/catch blocks to handle errors in async functions. How to use async/await you can refer to the below example. ``` var balance = 1000; function checkBalance() { return new Promise((resolve, reject) => { setTimeout(() => { if (balance != 0) { resolve(balance); } else { reject("Balance is 0") } }, 1000); }); } function withdraw(amount) { return new Promise((resolve, reject) => { setTimeout(() => { balance = balance - amount if (amount <= balance) { resolve(amount); } else { reject("Insufficient balance"); } }, 500); }); } function printReceipt(amount) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`Withdrawal Amount: ${amount} and your current balance is ${balance}`); }, 500); }); } async function withdrawMoney() { try{ console.log("withdrawing process started") let balance = await checkBalance() let withdrawalAmount = await withdraw(490) let receipt = await printReceipt(withdrawalAmount) console.log("receipt", receipt) }catch(error) { console.log(`Error: ${error}`) } } withdrawMoney() ```
Powering Innovation with a World-Class Tech Stack
At Nirman Labs, we harness a versatile array of technologies, from the reactive interfaces crafted with Vue.js and React.js to the scalable server-side solutions enabled by Go and Node.js. Our cloud deployments thrive on the robust infrastructure of AWS, complemented by the seamless container orchestration of Docker and Kubernetes.











Lorem ipsum dolor sit amet consectetur adipiscing elit semper dalar elementum tempus hac tellus libero accumsan.
“Lorem ipsum dolor sit amet conse ctetur adipiscing elit Vel mauris turpis vel eget nec orci nec ipsum Elementum felis eu pellentesque velit vulputate. Blandit consequat facilisi sagittis ut quis Integer et faucibus elemen.”
John Carter
Creative Director at Facebook
Industry Verticals served
Include travel agencies, tour operators, and stock trading apps for investors.
Project delivery
Projects are delivered with a focus on timely execution, quality assurance, and client satisfaction.
Clients returns for more projects
Additional projects, reflecting trust and satisfaction in our services.
Responses to common questions about our clients, industries, and project workflows.
We serve small to medium-sized companies seeking software consultation or development for their business needs. We also provide end-to-end solutions for specific problems and offer continuous support to startups requiring dynamic consultation and development.
We work across various sectors, including travel and tourism, transportation, interior home decor, and online business presence. We're adaptable and can tailor our solutions to meet the unique needs of different industries.
We start with a discovery call to understand your needs and the business context. After grasping the full scope, we provide estimations, quotations, and an ideal maintenance and support plan. For long-term engagements, we establish mutually agreed-upon processes and practices.
We have a strong understanding of data security and help our customers make informed decisions about hosting, processing, and managing sensitive data. We share our experience-led insights to ensure compliance with relevant regulations.
Yes, we offer customized post-launch support and maintenance plans tailored to the specific needs and nature of each solution.
We’re all ears, Your Vision, Our Expertise. Together, we can create the extraordinary. Reach out to Nirman Labs today to explore how our cutting-edge solutions can drive your business forward. Whether it’s a project inquiry, a potential partnership, or just a simple question, we’re here to listen and collaborate.