<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Practo Engineering - Medium]]></title>
        <description><![CDATA[How we build @Practo - Medium]]></description>
        <link>https://medium.com/practo-engineering?source=rss----b91421619e39---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Practo Engineering - Medium</title>
            <link>https://medium.com/practo-engineering?source=rss----b91421619e39---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 18 Jun 2026 02:01:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/practo-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Redirecting queries to Slave Database]]></title>
            <link>https://medium.com/practo-engineering/redirecting-queries-to-slave-database-3b0896f8911a?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/3b0896f8911a</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[mysql]]></category>
            <category><![CDATA[sqlalchemy]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[flask]]></category>
            <dc:creator><![CDATA[Ishan Joshi]]></dc:creator>
            <pubDate>Wed, 05 Feb 2020 06:07:38 GMT</pubDate>
            <atom:updated>2020-02-05T15:14:50.715Z</atom:updated>
            <content:encoded><![CDATA[<p>Master-Slave Databases are a common configuration of databases considering it allows replication making sure you don’t lose data in case of system failure. It also allows you to remove the burden from one database, and share it among many, making it an essential part of a good system. This feature can be especially useful for data analysis purposes.</p><p>Like any good system, we at Practo always have a database with a slave attached to it, and thanks to AWS RDS, we don’t have to worry about the data consistency across the master and slave database. This article aims to explain how to run a Query on the Slave Database with minimal code changes.</p><p>Before I dive into explaining the code, the application that we made this for is a Flask application with SQLAlchemy (<a href="https://www.sqlalchemy.org">https://www.sqlalchemy.org</a>) as an ORM.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TV5uVx354RZq8wwNxPN6KA.png" /></figure><p>SQLAlchemy is a great library, that allows developers leverage the full power and flexibility of SQL. One of the most important features of SQLAlchemy is the Query and the Session API. In short, the Queryclass is a SQL construction object, where you can generate a SELECT query programmatically. The Query object is also attached to the Session, which acts like an agent that helps you communicate with your database.</p><p>The Flask application we work on has a convention where all the Model Objects return Query Objects instead of actual data. This is useful for two main reason — firstly, this allows you to add filters in the later stages of the code, and, you can lazy load data, since, the Query object acts as a generator. In simple terms, the query is only executed when you request for data.</p><p>Although you could just execute the query string on the slave database, it would not provide us with lazy loading and options like adding filtering in the later stages of the code. So we needed to code something where we can still return a Query object, except instead of running the query on the Master Database, it should run it on the Slave Database. For this, we made use of Python decorators.</p><p>So lets go through the code:</p><ol><li>Create Session Object attached to the Slave Database:</li></ol><pre>from sqlalchemy import create_engine<br>from sqlalchemy.orm import sessionmaker<br>slave_db_engine = create_engine(&lt;URI for Slave Database&gt;)<br>slave_session = sessionmaker(bind=slave_db_engine)()</pre><p>2. Create the Decorator:</p><pre>from functools import wraps</pre><pre>from app import slave_session<br><br><br>def convert_to_slave_query(query_function):<br><br>    @wraps(query_function)<br>    def create_slave_query(*args, **kwargs):<br>        return slave_session.query(query_function(*args,<br>                                   **kwargs).subquery)<br>    return create_slave_query</pre><p>3. Use it in existing code (in this case, get_rick_characters):</p><pre>from util import convert_to_slave_query</pre><pre>@convert_to_slave_query<br>def get_rick_characters():<br>    return Rick.query()</pre><p>You can now easily use this decorator as a plugin and run queries on your slave database without having to go and edit existing code.</p><p>Generally, slave databases are used to run read-only queries. To avoid data manipulation to run on the slave database, you can use the following code:</p><pre>DATA_MODIFICATION_LITERALS = [<br>    &#39;update&#39;,<br>    &#39;delete&#39;,<br>    &#39;create&#39;,<br>    &#39;copy&#39;,<br>    &#39;insert&#39;,<br>    &#39;drop&#39;,<br>    &#39;alter&#39;<br>]</pre><pre>def _check_query_read_only(query):<br>    query_literals = str(query).split(&#39; &#39;)<br>    intersection = [<br>        literal.lower() for literal in query_literals<br>        if literal in DATA_MODIFICATION_LITERALS<br>    ]<br>    return len(intersection) == 0</pre><p>You can then change convert_to_slave_query decorator to:</p><pre>from app import slave_session<br><br>DATA_MODIFICATION_LITERALS = [<br>    &#39;update&#39;,<br>    &#39;delete&#39;,<br>    &#39;create&#39;,<br>    &#39;copy&#39;,<br>    &#39;insert&#39;,<br>    &#39;drop&#39;,<br>    &#39;alter&#39;<br>]</pre><pre>def _check_query_read_only(query):<br>    query_literals = str(query).split(&#39; &#39;)<br>    intersection = [<br>        literal.lower() for literal in query_literals<br>        if literal in DATA_MODIFICATION_LITERALS<br>    ]<br>    return len(intersection) == 0<br><br>def convert_to_slave_query(query_function):<br>    @wraps(query_function)<br>    def create_slave_query(*args, **kwargs):<br>        read_query = slave_session.query(query_function(*args,<br>                                   **kwargs).subquery)<br>        if not _check_query_read_only(read_query):<br>            raise Exception(&quot;Invalid Operation on Slave Database&quot;)<br>        return slave_session.query()<br>   return create_slave_query</pre><p>By doing so, you can be sure that only read queries run on your slave instance.</p><p>Although, you can now run queries on one slave database, this code does not allow you to run the query on multiple slave database, choosing the database using the Round Robin algorithm.</p><p>One more thing to keep in mind is that there is always some Replication Lag between Master and Slave instances of the Database, so it would be a bad idea to use this on any task that relies on latest data.</p><h3><strong>What’s Next?</strong></h3><p>Right now, the above code snippet supports reads only from one slave database. In future, this should be updated so that it supports n different slave database reads. Things like ReplicaLag, network proximity etc. can be used to select the correct database to run the query on.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b0896f8911a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/redirecting-queries-to-slave-database-3b0896f8911a">Redirecting queries to Slave Database</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Launching Worker Pod Autoscaler]]></title>
            <link>https://medium.com/practo-engineering/launching-worker-pod-autoscaler-3f6079728e8b?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/3f6079728e8b</guid>
            <category><![CDATA[autoscaling]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[queue]]></category>
            <category><![CDATA[sqs]]></category>
            <category><![CDATA[hpa]]></category>
            <dc:creator><![CDATA[Alok Kumar Singh]]></dc:creator>
            <pubDate>Sat, 14 Dec 2019 06:42:13 GMT</pubDate>
            <atom:updated>2019-12-14T06:42:14.033Z</atom:updated>
            <content:encoded><![CDATA[<h3>Launching Worker Pod Autoscaler — Solving specific problems with worker scaling in Kubernetes</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*NJoDmZ_kT9VIIHI5ORE3Qw.png" /></figure><p>When you are finding a doctor, booking an appointment or consulting a doctor online on Practo, a lot of work is done asynchronously using Job queue systems (a.k.a <strong>workers</strong>). This is a critical part of our architecture and it plays an important role in making our systems scalable and reliable.</p><p>We use <a href="https://aws.amazon.com/sqs/">AWS SQS</a> as the message queue and we process on a scale of about 800 million jobs per month. These workers are run as a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Kubernetes Deployment</a> with every worker running as a separate pod. This helps to deploy, scale, monitor and troubleshoot each worker process independently.</p><p>This article focuses only on the<strong> “scaling part”</strong> of these worker deployments.</p><h3>Implementation 1 — Using Custom Metrics and HPA</h3><p>When we started with Kubernetes we used <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">HPA</a> with its <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics">Custom Metric Support</a> and <a href="https://prometheus.io/">Prometheus</a> to scale the workers. This is also the default recommended solution from the community for scaling worker pods.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/583/1*uZ8c6hbMjVBAIA5KzhLXxQ.png" /></figure><p>Horizontal Pod Autoscaler(HPA) is a controller which scales Kubernetes Deployment typically based on CPU metric and supports scaling Kubernetes Deployment based on any custom metric. <strong>The only requirement is the user needs to make sure the custom metric is available for consumption by the Kubernetes process</strong>.</p><p>We exported the SQS Cloudwatch metrics to Prometheus by making the <a href="https://github.com/prometheus/cloudwatch_exporter">Prometheus Cloudwatch Exporter</a> compatible for this use case. Here is the code for it — <a href="https://github.com/practo/cloudwatch_exporter">https://github.com/practo/cloudwatch_exporter</a></p><p>This was our first implementation of scaling our background workloads. We ran this for about a year or so. Based on the feedback from the various service teams and our experience, the <strong>following were the problems with this implementation:</strong></p><ol><li>Slow scale-ups: A lot of services required scaling to be near real-time. If there is a surge in the messages in the queue then the workers should scale instantly. But this was not happening because the Cloudwatch metrics are <strong>only available at 5-minute intervals for SQS</strong>. Solving for this required a combination of long-polling, <a href="https://docs.aws.amazon.com/sdk-for-go/api/service/sqs/#SQS.GetQueueAttributes">SQS metrics API</a> and Cloudwatch metrics.</li><li>Scaling down was causing issues: <strong>HPA does not allow to scale up and down based on different metrics</strong>. But it can be different based on the use case. For example, we were scaling up on SQS metric — <strong>ApproximateNumberOfMessagesVisible </strong>but we could not scale down based on the same metric because for high throughput workers when they were consuming the queue very fast this metric was always zero.</li><li>Non-idle workers were scaling down: Since it was a target-based scaling when the Queue length metric dropped it started to scale down the workers which were not idle.</li><li>Costs were not optimized: Since metrics were polled from Cloudwatch at fixed intervals and not intelligently as required, this incurred a significant AWS cost towards Cloudwatch. This can be optimized by fetching only the relevant metrics only when we require them.</li><li>Using custom metrics was not as straightforward as running a few kubectl commands and required a good effort from the user to make the whole setup work. Also, <strong>HPA was requiring us to decouple the metrics from the scaler which means we cannot derive the scaling metrics using the currently running pods already known to the scaler</strong>.</li></ol><p>So to solve the above problems, we wrote <a href="https://github.com/practo/k8s-worker-pod-autoscaler">Worker Pod Autoscaler.</a></p><h3>Implementation 2 — Worker Pod Autoscaler</h3><p><a href="https://github.com/practo/k8s-worker-pod-autoscaler">practo/k8s-worker-pod-autoscaler</a></p><p>Scaling workloads based on queue length is a fairly common use case. Therefore we decided to build a generic, performant and very easy to use open-source autoscaler for workers using <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/">Kubernetes CRDs</a>. Following were the primary goals of this project:</p><ol><li>Superfast scale-ups: Near real-time scaling of workers. As soon as a job comes in the queue the containers should scale if needed.</li><li>Solve the scale down problems for the workers: The solution should not scale down workers that are processing a job. Scale down metric should not be restricted to the same metric as scale-up, it should be based on the queue provider.</li><li>Built-in Queue Exporters: Kubernetes HPA with custom metrics requires org effort in exporting custom metric and storing them to be made accessible to the Kubernetes API. This makes sense for Kubernetes to support several use cases. But the current HPA implementation is restrictive to use for queue-based scaling. Also, <strong>built-in</strong> help in focussing the user worry about the application and frees the user from maintaining metric exporters. The project should build generic metric exporters (pollers) with every queue provider integration.</li><li>Support for scaling up from and down to zero pods to optimize costs.</li><li>Ease of Use:<strong> The whole setup of worker autoscaling should start working by running a few kubectl commands.</strong></li><li>Generic Platform Independent Autoscaler: The autoscaler should extend to support any message queuing service and should work in any cloud or on-premise Kubernetes environment.</li><li>Open Source: The solution should be completely open-sourced for a bigger community to use, contribute and make it better.</li></ol><p>Now, let’s dig into how Worker Pod Autoscaler aka WPA works!</p><h3>Worker Pod Autoscaler</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/662/1*GYE9qyNEmbz_Zw0xltY-Yw.png" /></figure><p>When the WPA controller pod starts it install the WPA custom resource in Kubernetes if it is not present and it starts the following go-routines:</p><ul><li>WPA Goroutine: WPA controller go-routine is the control loop which scales the deployment to the required number of replicas based on the queue specified for the deployment.</li><li>Poller Goroutine(s): There is a <a href="https://github.com/practo/k8s-worker-pod-autoscaler/blob/6451f18135b9489948b275b038cfe0ae60ead01a/pkg/queue/poller.go#L81-L109">poller manager</a> who starts and stops go-routines (poller) for each WPA resource. Each poller polls the queue provider metrics for the exact metric keeping the API calls to minimum. The sync intervals here can be configured based on use cases. The concurrency here is one of the reasons for near real-time scaling.</li></ul><p>The algorithm for deciding the desired replicas can be read h<a href="https://github.com/practo/k8s-worker-pod-autoscaler/blob/bf3ef92082ae7032ce32f771f7b292fb30c21c43/pkg/controller/controller.go#L355-L397">ere.</a></p><p>Explaining WPA working with a simple example.</p><h4>Install WPA</h4><p>Worker pod autoscaler can be easily installed using <em>hack/install.sh</em>. Please <a href="https://github.com/practo/k8s-worker-pod-autoscaler#install-the-workerpodautoscaler">follow this</a> for detailed information.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7TJ2wa85rdPvslMPKqzYBg.png" /></figure><h3>Example</h3><p>Create a deployment and a WPA resource to manage the scaling of it.</p><pre>kubectl create -f artifacts/example-deployment.yaml<br>kubectl create -f artifacts/example-wpa.yaml</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t8DkK8XwHlEZr3tej-AUHw.png" /></figure><p><strong>WPA will start scaling example-deployment from 0 to 30 if the messages in the queue are greater than 150. </strong>The workers will scale down to 0 when there are no jobs in the queue.</p><p><strong>That’s it! Two kubectl commands and your worker deployment starts scaling!</strong></p><p>We have been running WPA in production from <a href="https://github.com/practo/k8s-worker-pod-autoscaler/releases/tag/v0.1.0">the last 4 months</a>. We have got great feedback from our developers in Practo. It is now in <a href="https://github.com/practo/k8s-worker-pod-autoscaler/releases/tag/v0.2.2">v0.2.2 release</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-53gTvemBnUw0WMu_TZuYQ.png" /><figcaption><strong>Unused workers were stopped reducing memory usage from 2 GB to 100 MB for one of the services.</strong></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XSNQzSmt9O0pvVWfTt3Zmw.png" /><figcaption><strong>Cloudwatch costs came down by 50% because only relevant metrics are fetched more intelligently.</strong></figcaption></figure><h4>Future Work</h4><p>SQS is the only queue provider supported at present. But it can easily be extended to any other queue provider. <a href="https://github.com/practo/k8s-worker-pod-autoscaler/blob/9e7cd690da1c8c0f6aa3490853f7e805d629b4bd/pkg/queue/queueing_service.go#L5-L8">This interface</a> needs to be implemented and a <a href="https://github.com/practo/k8s-worker-pod-autoscaler/blob/9e7cd690da1c8c0f6aa3490853f7e805d629b4bd/pkg/queue/sqs.go">simple poller like this</a> needs to be written down to bring in a new queue provider support! Pull requests are welcome :)</p><p>Thanks to the Kubernetes team for making <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/">CRDs</a> and <a href="https://github.com/kubernetes/sample-controller">sample controller</a>. Please feel free to open issues in <a href="https://github.com/practo/k8s-worker-pod-autoscaler/issues">Github</a> if you have any questions or concerns. We hope WPA is of use to you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3f6079728e8b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/launching-worker-pod-autoscaler-3f6079728e8b">Launching Worker Pod Autoscaler</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serverless flows with Step Functions]]></title>
            <link>https://medium.com/practo-engineering/serverless-flows-with-step-functions-bac062f8c625?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/bac062f8c625</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[serverless]]></category>
            <category><![CDATA[aws-step-functions]]></category>
            <category><![CDATA[lambda-function]]></category>
            <dc:creator><![CDATA[Sahana Bhat]]></dc:creator>
            <pubDate>Mon, 22 Apr 2019 11:09:47 GMT</pubDate>
            <atom:updated>2019-04-22T11:09:47.342Z</atom:updated>
            <content:encoded><![CDATA[<p>Serverless paradigm is taking Iaas (Infrastructure as a Service) to the next level in the form of Faas (Function as a Service). Its gaining popularity is because of the many benefits that comes with it, like, cost reduction — because you only pay for the amount of time your code is running, fast deployments and managed autoscaling — because you do not maintain a dedicated server for your application, etc.</p><p>AWS Lambda Functions, Google Cloud Functions, Azure Functions are some of the serverless hosting options provided by the cloud providers. Some CDNs provide this option too, for example, Workers on CloudFlare. The added advantage of hosting your code on CDN is latency reduction, since the user is served by the CDN server closest to the user. That being said, serverless architecture might not be ideal for every use-case of yours. For your long running processes or applications with continuous traffic, cost would increase and so, you are better off spinning off a server to host your process or app.</p><p>In the micro-services and serverless era, if your infrastructure revolves around AWS, Lambda functions are one of the most popular and easiest options to host your micro-services or backend processes . Through Lambda functions, AWS enables you to host your code, without you managing the infrastructure (like an EC2). Lambda functions can be triggered by hooks like API gateway, S3 events or one of the many CloudWatch events available.</p><p>If your use case is solved by a single Lambda function, setting up the entire flow is a piece of cake. If not, your multiple Lambdas will have to be glued together. Let’s look at a hypothetical use case of automating the task of validating the key-pair attached to any new EC2 instance launched in your account and then notifying a concerned person, if it is an outdated/invalid key-pair. For the sake of separation of concerns, let’s have 2 Lambda functions — one to validate the key-pair and the second to notify. How we typically would implement this, is as follows.</p><ol><li>Create a Cloudwatch rule to receive events of EC2 creations.</li><li>Create a Lambda function, let’s call it the-validator, and add it as a trigger to the rule created in Step 1.</li><li>If the key-pair is determined to be invalid, the-validator would push a job to an SQS queue (or an SNS topic or any other trigger for lambda).</li><li>The second Lambda function, let’s call it the-notifier, would be added as a trigger to the SQS/SNS of Step 3 and it would notify whenever there is a new event.</li></ol><p>Though the above setup seems straight forward, the lambda-SQS-lambda pipeline would increase as and when we add more concerns to the flow. Step functions provide a graceful way to glue your lambdas together. Step functions is to lambdas, what conjunctions is to grammar. A Step function constitutes a state machine which holds the definition of how the Lambdas are inter-connected. Below is the state machine which would create a step function to handle the use case discussed above.</p><pre>{<br>  &quot;StartAt&quot;: &quot;the-validator&quot;,<br>  &quot;States&quot;: {<br>    &quot;the-validator&quot;: {<br>      &quot;Type&quot;: &quot;Task&quot;,<br>      &quot;Next&quot;: &quot;the-notifier&quot;,<br>      &quot;Resource&quot;: &quot;arn-of-the-validator-lambda&quot;<br>    },<br>    &quot;the-notifier&quot;: {<br>      &quot;Type&quot;: &quot;Task&quot;,<br>      &quot;End&quot;: true,<br>      &quot;Resource&quot;: &quot;arn-of-the-notifier-lambda&quot;<br>    }<br>}</pre><p>The above state machine and the lambdas are the only resources required to implement the discussed use case. This state machine is added as the trigger to the CloudWatch rule for EC2 creation events, which means, when a new EC2 is created, this state machine is executed. State machines are built to pass on the output of one step to the next. In our case, the output of the-validator would be passed to the-notifier, with no extra configurations to be done.</p><p>The above state machine can be extended to add more states when the concerns increase, eliminating the need to create complex workflows with SQS, SNS or any other glue in picture. Let’s say, a 3rd Lambda is added to our above discussed use case. This new Lambda, let’s call it the-db-handler, would save the invalid key-pair and the EC2 details in persistent storage. Here is our updated state machine.</p><pre>{<br>  &quot;StartAt&quot;: &quot;the-validator&quot;,<br>  &quot;States&quot;: {<br>    &quot;the-validator&quot;: {<br>      &quot;Type&quot;: &quot;Task&quot;,<br>      &quot;Next&quot;: &quot;the-notifier&quot;,<br>      &quot;Resource&quot;: &quot;arn-of-the-validator-lambda&quot;<br>    },<br>    &quot;the-notifier&quot;: {<br>      &quot;Type&quot;: &quot;Task&quot;,<br>      &quot;Next&quot;: &quot;the-db-handler&quot;,<br>      &quot;Resource&quot;: &quot;arn-of-the-notifier-lambda&quot;<br>    },<br>    &quot;the-db-handler&quot;: {<br>      &quot;Type&quot;: &quot;Task&quot;,<br>      &quot;End&quot;: true,<br>      &quot;Resource&quot;: &quot;arn-of-the-db-handler-lambda&quot;<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uqeOcCYydPdLw3PXffMeZg.png" /><figcaption>Ways to glue Lambdas</figcaption></figure><p>The new Lambda and addition of the state to the state machine are the only changes required to update the flow making it very straightforward to add more functionality to your system. the-notifier, which was the end state of the state machine earlier, now specifies that the-db-handler is the next state and the-db-handler is the new end state.</p><p>Creating a workflow for your lambdas is one of the basic use cases of step functions. It also has a lot more useful features like conditional states, wait states, parallel states execution, etc. Though step functions isn’t a technical marvel, it does make the serverless experience better and is worth a try for your serverless workflows.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bac062f8c625" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/serverless-flows-with-step-functions-bac062f8c625">Serverless flows with Step Functions</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Migrating to WebPack 4 for Beginners]]></title>
            <link>https://medium.com/practo-engineering/migrating-to-webpack-4-for-beginners-e52e49753235?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/e52e49753235</guid>
            <category><![CDATA[beginner-javascript]]></category>
            <category><![CDATA[webpack]]></category>
            <category><![CDATA[webpack-3]]></category>
            <category><![CDATA[beginners-guide]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Karan Jariwala]]></dc:creator>
            <pubDate>Thu, 04 Apr 2019 05:53:41 GMT</pubDate>
            <atom:updated>2019-04-04T05:53:41.174Z</atom:updated>
            <content:encoded><![CDATA[<h4>Why and how to migrate to web-pack 4</h4><h3>Why migrate?</h3><p><strong><em>Web-pack is our static module bundler for modern JavaScript applications and has become a core part of how we bundle them.</em></strong></p><h3>🐌 Slow Build: T<strong>he Main Reason.</strong></h3><p>We have a fairly complex app with lot of entry points, also each entry point can have multiple output files considering for i18n translations. Also, we have neglected our build process for some time now. Result? Our build was slow!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c6jCnQ-0VyLbsYyRhdYnxQ.png" /><figcaption>149 seconds to dev — build with Web-pack 3</figcaption></figure><h3>🏎How to Speed Up the Build?</h3><ol><li><strong><em>Legato (Web-Pack 4) is claimed to be 98% faster. Migrating to Web-Pack 4 can itself can help you loose those extra seconds. 😅</em></strong></li><li><strong><em>Migrating exercise will also help you deeply understand the build process of your existing code base. It will give you ability to refactor and further optimise your web-pack build. At the very least, it will help you make a todo list of things you can improve on in your build.</em></strong></li></ol><h3>After migrating to Web-Pack 4 and some easy refactoring, the build time is reduced by 60%.</h3><h3>From 159 seconds to 60 seconds! 🏎🏎🏎</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X9Cdox_IsjNLfiWo8Y0PXA.png" /></figure><h3>How to Start Migrating to Web-pack 4?</h3><ul><li>Read the <a href="https://github.com/webpack/webpack/releases">change-log</a>, as to know what has changed and what’s new. When migrating to a new major release, this should be the first thing to look for in any open source project.🕵️‍🕵️‍</li><li>Know your existing web-pack build process inside-out, find out what entry, output, loaders, plugins are and how it is currently being used.</li></ul><h4><strong>Before migrating, try to refactor/optimise your build code.</strong></h4><ul><li>Instead of one big <em>webpack.config.js, it is better to separate all the functions used to say build-utils. All the constants in build constants.</em></li><li><em>Separate the dev config from the prod config.</em></li><li>If you have multiple apps (multiple entry points) in your codebase, in development one would ideally work only on one app at a time. So, make sure your dev build is only building that one app instead of all. <br>You can write a simple function which return only one filtered entry point in development. <strong><em>Less stuff to build, faster your build!</em></strong> 🏎🏎🏎</li><li>If you are doing i18n by generating a output file for each locale you have to translate. While developing, make sure you are not generating more than one output file. <strong><em>Less stuff to build, faster your build!</em></strong> 🏎🏎🏎</li></ul><h4><strong>After you are satisfied with refactor and optimisations</strong></h4><ul><li><strong>Add latest web-pack to your project. Currently it is 4.16.0.</strong></li></ul><pre>yarn add webpack@latest --dev</pre><ul><li><strong>Add web-pack CLI.</strong></li></ul><pre>yarn add webpack-cli --dev</pre><ul><li><strong><em>Provide Mode to web-pack.</em></strong></li></ul><p><em>Possible values for </em><em>mode are: </em><em>none, </em><em>development or </em><em>production(default). </em>Providing the mode configuration option tells web-pack to use its built-in optimisations accordingly.</p><p>You will have to pass mode in npm script or add it in webpack.config.js</p><pre>webpack --mode=production</pre><p>OR</p><pre>//webpack.config.js</pre><pre> module.exports = { <br>   mode: &#39;production&#39;, <br>   ...<br> };</pre><h3>Most probably you would have used CommonChunks plugin. ✋✋</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h2rByz3GYSToW7xY5qB2yA.png" /><figcaption>CommonsChunk plugin deprecated in web-pack 4</figcaption></figure><ul><li><strong>CommonChunks<em> will have to be replaced to use the new optimization.splitChunks.</em></strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LIE61g_QsdxUCCrEgv9Snw.png" /><figcaption>In Web-pack 4 generating a vendor and manifest without CommonsChunk plugin</figcaption></figure><h3><strong>Now, the most important step!</strong></h3><ul><li><strong>Set process.traceDeprecation to true this will help you to find which packages are using deprecated API</strong></li></ul><pre>node --trace-deprecation ./node_modules/.bin/webpack</pre><p><em>This can also be set from </em><strong><em>DefinePlugin</em></strong></p><pre>new webpack.DefinePlugin({<br>&#39;process.traceDeprecation&#39;: true,<br>})</pre><p>Most likely, the Node packages still using the old API will throw the below error.</p><pre>DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead</pre><p><strong><em>You can simply upgrade the package one by one</em></strong></p><pre>yarn add &lt;package&gt;@latest --dev</pre><p><strong><em>99% of the times upgrading will solve your error. It did for me! 😅</em></strong></p><p><strong><em>In that 1%, where just upgrading package to the latest version does not solve the problem, you would have to fix the issue yourself or raise an issue in the open-source project and wait till some-one else fixes it. 😥</em></strong></p><p>If you liked reading this, don’t forget to clap. 👏👏</p><p>You can also follow me on on twitter @<a href="https://twitter.com/karanjariwala47">karanjariwala47</a> for java-script or react updates.</p><p>Thank-you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e52e49753235" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/migrating-to-webpack-4-for-beginners-e52e49753235">Migrating to WebPack 4 for Beginners</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing Redux-Sagas with a plan.]]></title>
            <link>https://medium.com/practo-engineering/testing-redux-sagas-with-a-plan-e59124c5d139?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/e59124c5d139</guid>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[ui]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux-saga]]></category>
            <category><![CDATA[test]]></category>
            <dc:creator><![CDATA[Karan Jariwala]]></dc:creator>
            <pubDate>Thu, 04 Apr 2019 05:53:11 GMT</pubDate>
            <atom:updated>2019-04-04T05:53:11.548Z</atom:updated>
            <content:encoded><![CDATA[<h4>Why Saga Testing and how?</h4><blockquote>Redux Saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, simple to test, and better at handling failures.</blockquote><blockquote>— Redux Saga Docs</blockquote><p>Today, we use Redux-Saga heavily in our enterprise application. Most of the logic and all of the side-effects are handled by our Sagas. Our Components and Reducers are mostly dumb.</p><h3>We treat the root Saga like a separate thread in our application. Any action that can lead to a side effect is handled by our saga thread.</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fc1DqzI4gnaWLdPKD86WgQ.png" /><figcaption>Mental Model of how our UI works.</figcaption></figure><p>From a testing perspective, the main advantage of this architecture is that all our side effects and logic conditions are in one place i.e Sagas. Testing Sagas alone would give us much greater confidence than testing reducers or components. So, testing Sagas is an imperative for us.</p><p><strong>There are two ways to test sagas.</strong></p><h3>Stepping through each <strong><em>yield</em></strong> in Saga one by one</h3><p>With this approach, you will step through <em>yield</em> effects one by one. Typically, in the exact order by asserting the effect objects at each step. This is like unit testing your sagas. <strong><em>Though straight forward, this will make your tests brittle</em></strong>. As your tests are tightly coupled with the implementation and also the order of effects. Any refactoring or restructuring will most likely break your tests.</p><h4>So, we decided to go with the second option.</h4><h3>Testing not how a saga is implemented but only the effects that you’re interested in.</h3><p><strong><em>Here you will run the saga to the end, mocking select and call effects and asserting the put effects you are interested in.</em></strong></p><p>This is more towards integration testing. As you are not running your saga effects in isolation one after the other, this type of test should give you more confidence.</p><p>The main benefit, however, is your tests are not <strong><em>Brittle</em></strong>. They are not tightly coupled with the implementation. Example: If you re-order your effects or you add one unrelated <strong><em>put</em></strong> effect for a corner case, it should not break your test.</p><blockquote>While both of the above testing methods can be written natively, there exist several libraries to make both methods easier. For testing each generator yield step-by-step there is <a href="https://github.com/stoeffel/redux-saga-test">redux-saga-test</a> and <a href="https://github.com/antoinejaussoin/redux-saga-testing/">redux-saga-testing</a> for option one. <a href="https://github.com/wix/redux-saga-tester">redux-saga-tester</a>, <a href="https://github.com/jfairbank/redux-saga-test-plan">redux-saga-test-plan</a> and <a href="https://github.com/DNAinfo/redux-saga-test-engine">redux-saga-test-engine</a> for option two. You can also unit test with <strong>test plan and tester.</strong></blockquote><blockquote>— Redux Saga Docs</blockquote><p>We decided to go with <strong><em>Redux-saga-test-plan.</em></strong></p><h4>Let’s test a basic saga worker which is written for the requirements below.</h4><blockquote><strong><em>If user is not logged in, he should be redirected to login.</em></strong></blockquote><blockquote><strong><em>If user is logged in and does not have a right to open the Modal, he should be denied access.</em></strong></blockquote><blockquote><strong>If user is logged in and has the right to open the Modal, Modal opens.</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*djxdRBhrZqfB0UpxlrQvXw.png" /><figcaption>Trigger Modal Saga</figcaption></figure><p>Our TriggerModal Saga worker when triggered by an action selects the UserId and the ModalRight from the Redux store using the select effect.</p><p><strong><em>If UserId is not falsy, it means user is logged in.</em></strong></p><p><strong><em>The ModalRight is true, if he has the action right and false if he does not.</em></strong></p><h4>We have to test our saga worker in three different redux states where:</h4><h4>1. User is not logged in</h4><h4>2. He is logged in but does not have the right</h4><h4>3. He is logged in and has the right.</h4><p>Our three Mocked states look like these.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WhjdO0b-obZt4culvXyh5A.png" /><figcaption>States mocked to test saga.</figcaption></figure><h3>We intend to test the behaviour not the implementation.</h3><p><strong><em>For the above three states we have three cases.</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jh5g7xODgsGDUODcSEoPhw.png" /><figcaption>Saga effect testing</figcaption></figure><p><strong><em>For all the three cases, we run the saga and pass the corresponding redux states. We then mock the </em></strong><strong><em>select effects and assert what the </em></strong><strong><em>put effects should be.</em></strong></p><p><strong><em>We are only asserting what comes out of saga in a particular state of application there by testing only the behaviour and not the implementation.</em></strong></p><p>Here, even if you change the order of select effects in the saga or add a new independent effect it will not necessarily break the Test.</p><p>Also after .run. you could do .then and assert with even more control on values of effect objects.</p><p>For example, in each of the above cases we know only one put should be dispatched. We can assert the same using .then as shown below</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nXUInz5UNkBenbBfyvRWyw.png" /><figcaption>Asserting when saga has only one PUT effect object</figcaption></figure><p>Read More about testing sagas using redux-saga-test-plan <a href="http://redux-saga-test-plan.jeremyfairbank.com/">here</a></p><p>If you liked reading this, don’t forget to click 👏 and add claps for the post. You can also follow me on on twitter @<a href="https://twitter.com/karanjariwala47">karanjariwala47</a> for java-script or react updates.<br>Thank-you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e59124c5d139" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/testing-redux-sagas-with-a-plan-e59124c5d139">Testing Redux-Sagas with a plan.</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Track Android uninstalls for less than $1 a month]]></title>
            <link>https://medium.com/practo-engineering/track-android-uninstalls-for-less-than-1-a-month-c4657e2465c6?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/c4657e2465c6</guid>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[uninstall]]></category>
            <dc:creator><![CDATA[Nishant Shah]]></dc:creator>
            <pubDate>Thu, 20 Dec 2018 05:40:21 GMT</pubDate>
            <atom:updated>2018-12-20T05:40:21.158Z</atom:updated>
            <content:encoded><![CDATA[<h3>Track Android uninstalls with user details for less than $1 a month</h3><p>It takes a lot of efforts and time to build and launch an app. It takes even more efforts to make users install the app. If users are leaving your app, you would immediately want to know why.</p><p>The main motive behind tracking uninstall rate is to figure out what made users delete the app. Uninstall tracking can also help us identify users who uninstalled the app and retarget them to install the app again or even to ask the for feedback. This tutorial will help you track uninstalls and find events performed before the uninstall happened using Firebase.</p><p>Firebase automatically tracks ‘<a href="https://support.google.com/firebase/answer/6317485?hl=en">app_remove</a>’ events when an application package is removed or “uninstalled” from an Android device. But the problem with Firebase console is that it only shows the number of events. It does not share the user’s demography or events performed before the uninstall happened.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h3wpoEOh7zz3AP3OdrT1lw.png" /><figcaption>Automatically tracked events Firebase</figcaption></figure><p>To find users demography and events performed before uninstall, we need to export all analytics event from firebase to BigQuery. Linking BigQuery to Firebase app lets us access our raw, unsampled event data along with all of the parameters and user properties. To transmit Firebase data to BigQuery, you must upgrade your project to the Blaze plan. We’ll talk more about costs later in the costs section.</p><h3>Setup</h3><p>First of all, we’ll have to integrate firebase with our Android project. Follow the instructions mentioned here to <a href="https://firebase.google.com/docs/android/setup">add firebase to your Android Project</a>. Add <a href="https://firebase.google.com/docs/analytics/android/start">Fireabase analytics</a> to your project and start tracking events which you would like to track before the uninstall happens.</p><blockquote>Do not forget to set <a href="https://firebase.google.com/docs/analytics/android/properties">user properties</a> in analytics events. It’ll help you identify users who uninstalled the app and retarget them.</blockquote><p><strong>Step 1:</strong></p><p>Upgrade your Firebase to Blaze</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dmKfF27LMk2X1DKOb_XR3w.png" /><figcaption>Firebase plans</figcaption></figure><p><strong>Step 2:</strong></p><p>Link BigQuery to Firebase. To link BigQuery to your Firebase project:</p><ul><li>Sign in to Firebase</li><li>Click the Settings icon and select Project Settings</li><li>On the Project Settings page, click the Integrations tab</li><li>On the BigQuery card, click Link</li><li>Once linking is complete, find the BigQuery card in integration tab, click on Manage</li><li>Under Firebase analytics flow, turn on the flow to export data of your app</li></ul><p>It will take some time to export start exporting data from firebase to BigQuery. Once your data is loaded into BigQuery, you can run SQL-like queries over it using BigQuery. Here is the sample query which we ran to analyze the events before the uninstall happened. For more information, see Querying Data in the <a href="https://cloud.google.com/bigquery/querying-data">BigQuery documentation</a>. If you have added user properties in your analytics events, you’ll be able to identify users who had uninstalled the app and will be able to retarget them</p><p><strong>Step 3:</strong></p><p>Query raw analytics evens in BigQuery. Here is the sample query to identify users who uninstalled the app on 2018/12/03</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_yKE5X_lc663ppbeFBxz5w.png" /><figcaption>Sample query 1</figcaption></figure><p>There’s a lot more stuff you can do by querying your raw data. Here is another sample query to find the last two events performed by the users before uninstalling the app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iIbr1g7QQZ8p2Yjh92Jg4Q.png" /><figcaption>Sample query 2</figcaption></figure><h3>Cost</h3><p>As we discussed, to transmit Firebase data to BigQuery, you must upgrade your project to the Blaze plan. Blaze plan is a postpaid plan, you’ll only pay for what you use. No fixed monthly charges either. For more information, see <a href="https://firebase.google.com/pricing/">Firebase pricing</a>.</p><p>In addition, storing and querying data in BigQuery incurs a fee. For more information, see <a href="https://cloud.google.com/bigquery/pricing">BigQuery pricing</a>.</p><p>To reduce data storage costs even further as daily event tables accumulate, delete older tables or move them to Cloud Storage.</p><blockquote><strong>How much does it cost us?</strong></blockquote><blockquote><em>Well, less than $1.00 per month💸💸. We log around 70 events, 3 user properties and store only last 6 months data and we do not use any other paid feature of Firebase Blaze plan</em></blockquote><p>It takes time to build an app that everyone likes. Until then, we have to keep a watch on users’ actions within our app and discover the problem which led to uninstalls and of course fix that problem.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4657e2465c6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/track-android-uninstalls-for-less-than-1-a-month-c4657e2465c6">Track Android uninstalls for less than $1 a month</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Connecting you to The best Doctor in 60 seconds: Demystifying Practo Consult]]></title>
            <link>https://medium.com/practo-engineering/connecting-you-to-the-best-doctor-in-60-seconds-demystifying-practo-consult-791c437cf74a?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/791c437cf74a</guid>
            <category><![CDATA[health-technology]]></category>
            <category><![CDATA[health]]></category>
            <category><![CDATA[consultation]]></category>
            <category><![CDATA[healthcare]]></category>
            <category><![CDATA[practo]]></category>
            <dc:creator><![CDATA[Sajal Sarwar Sharma]]></dc:creator>
            <pubDate>Tue, 18 Dec 2018 11:45:27 GMT</pubDate>
            <atom:updated>2018-12-18T11:45:27.420Z</atom:updated>
            <content:encoded><![CDATA[<h3>Connecting you to the right doctor in 60 seconds or less: Demystifying online consultations</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6FON-FzLT2DCQfI2TY-tNA.png" /></figure><blockquote>On a merry Saturday evening you suddenly come down with a fever. The shivering is incessant and nausea is building up too. Things could have been better but they are not bad either, until someone brings up the Swine Flu outbreak in the city. You start panicking. A cab is booked for the nearest hospital but you are stuck in the infamous traffic.</blockquote><blockquote>Anxiety is through the roof so you search the internet about your symptoms. Your heart sinks when internet informs you of the possibility of a terminal disease. You search again and find Practo’s online consultation service. 3 clicks and 2 minutes later you are talking to an actual Doctor, who tells you that your fever is fairly ordinary (terminal only if a bear eats you). You reach the hospital with far less anxiety and a lot more reason.</blockquote><p>The above is not a thriller script. It is just a dramatized version of a real-life account from a real Practo user.</p><p>Now you probably have 2 questions</p><p><strong>FIRST: What is Practo?</strong></p><p>Practo is a noble idea that took shape in 2008, in a college dorm in India, when one of the two founders couldn’t share his father’s medical reports with a doctor in the US.</p><p>Ever since then, Practo has devised numerous solutions for doctors and patients to make their lives easier. Following are just a few of their many offerings:</p><ol><li>An online doctor appointment booking platform</li><li>Online consultation through text, call or video chat</li><li>Diagnostic tests with home sample collections facility</li><li>A slew of software for clinics, hospitals, and multi-chain clinics</li></ol><p><strong>[</strong>Visit practo.com for a complete rundown on all of her products and services.<strong>]</strong></p><p><strong>SECOND QUESTION: How do you consult online?</strong></p><p>Practo offers an online doctor consultation service where patients can talk to specialists across many specialties.</p><h4><strong>It’s the next best thing to visiting a doctor physically.</strong></h4><p>Less than 2-min response time <strong>| </strong>In-chat report sharing <strong>| </strong>100% secure</p><h3>“How do you ensure 2-min responses?”</h3><p>The first step of initiating an online consultation requires patients to elaborate on their health concern. Our automation system predicts the problem area and the related speciality. Later they proceed to pay the marginal consultation fee, after which our algorithm kicks in. It searches our database for the doctors who meet certain minimum criteria.</p><p>e.g. instant online availability, feedback received from previous consultations, general response time, experience in the field etc.</p><p>Next, we send notifications to these doctors on our Provider app about the potential consultation. These notifications are sent to different but relevant group of doctors until one accepts the request. In approximately 97–98% of the cases, one of the doctors in the group accepts the notification and directly connects with the patient.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a8UoBfmCpgyGnfbguItCaQ.png" /></figure><p>Since the doctors accept the consultation directly, it implies that they can reply to the user instantly and in most of the cases, within a minute. In case the doctor is unable to reply to the patient’s messages, our system sends prompt reminders through repeated notifications.</p><p>Thousands of people use this feature to find solutions to nagging health problems. So, we work day and night to make it even more life-like. Recently, we have added video chat to the text so that the patients can literally <em>see </em>a doctor. (The video and audio calls in the text are free and the user don’t have to pay any extra amount).</p><p><em>Practo was ground up by some of the best people in the industry. And, its online consultation service has seen a hockey-stick growth in the last 2 years, and the credit for that goes to the Product, Engineering and Operations team working together day and night to give the best UX/UI to the anxious consumer (patient).</em></p><p>Want to give it a try? Just hit the following link:</p><p><a href="https://chat.practo.com">Ask A Doctor Online And Get Answers 24X7 | Online Doctor Consultation | Practo</a></p><p>Curious to understand the nuances of the feature? If you are in Bangalore and around JP Nagar, just ping me. I will be happy to indulge in a techie chat.</p><p>Disclaimer: The algorithm is a copyright of Practo and completely confidential. Excuse the spy- level discretion employed in the article. However, if you have more ideas and would like to use them to make the world better and healthier, join us!</p><p>Special Note: Thanks to <a href="https://medium.com/u/43fba91757a2">Mahima Singh</a> for taking time out and almost co-writing the article.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=791c437cf74a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/connecting-you-to-the-best-doctor-in-60-seconds-demystifying-practo-consult-791c437cf74a">Connecting you to The best Doctor in 60 seconds: Demystifying Practo Consult</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Inter-service authentication and the need for decentralised shared key exchange]]></title>
            <link>https://medium.com/practo-engineering/inter-service-authentication-and-the-need-for-decentralised-shared-key-exchange-29fda3e7e1e0?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/29fda3e7e1e0</guid>
            <category><![CDATA[decentralised]]></category>
            <category><![CDATA[inter-service-auth]]></category>
            <category><![CDATA[diffie-hellman]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[request-in-request]]></category>
            <dc:creator><![CDATA[Krupa Sagar]]></dc:creator>
            <pubDate>Tue, 13 Nov 2018 15:10:17 GMT</pubDate>
            <atom:updated>2019-01-19T10:32:01.247Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>TL;DR</strong> Decentralised inter-service authentication is easy to implement, reduces maintenance woes and more secure. It uses Diffie-Hellman and Request-In-Request mechanisms. (Scroll down for the all-explaining image)</p><p><strong><em>Note</em></strong>: In this post, I will be stressing on the word <strong><em>decentralised</em></strong> due to the fact that most of the teams/projects use centralised solution for key distribution and we will look into why centralised solution can create a havoc!</p><h4>Lets start with the few reasonable assumptions</h4><ul><li>We all know what is inter-service communication since almost everyone has adopted or read about service oriented architecture and amazing benefits it brings to a system.</li><li>We have encapsulated micro-services (Ideal scenario) which communicates with each other over some protocol (Such as HTTP, RPC, WebSockets, MQTT, and AMQP).</li><li>We use a secure channel (such as HTTPS) for communication.</li></ul><h3>Now lets reiterate the problem at hand and the probable solutions.</h3><p>Though the communication within the channel is secured i.e. HTTPS channel, if anyone is aware of the micro-service resource URL then a simple POST request can alter the state of the system or a simple get could leak the data i.e. core to an organisation and we just can’t afford that.</p><p>The above problem can be solved in many different ways and lets concentrate on the prominent ones.</p><ul><li>Filter traffic to the service</li><li>Pre-establish shared secret between micro services.</li><li>Centralised key store service/database where every other service is connected to fetch the latest secret for encrypting the data.</li><li>Decentralised key establishment between micro services with TTL.</li></ul><p>Now lets evaluate each of the solutions and try to come up with the best possible way for secured inter-service communication.</p><h4>Filter traffic to the service</h4><p>This can be achieved using an IP whitelist or by deploying the application in a private cloud where external access is restricted etc.</p><p>This is the first level of defense for every application and creates a wall between <strong>trusted network and untrusted internet</strong>. But, this approach falls short if different micro-services have different levels of security and importance. If one micro-service is compromised, it should not bring down the whole system along with it.</p><h4><strong>Pre-establish shared secret between micro services</strong></h4><p>Key shared over a file is not safe for long (<strong><em>Mainly due to human involved while key establishment</em></strong>), so we need to rotate these keys with new ones often. Whenever new keys are established that may requires a deployment and/or a planned downtime which isn’t a bad solution but it’s just inefficient and resource (human and the rest) intensive over time and we need to stop relying on human presence for key establishment.</p><h4>Centralised key store service</h4><p>After looking into the pros and the cons of above mentioned solutions, the first viable solution that comes to our mind is to build a key exchange service where every other service communicates with to fetch a shared key between any two services.</p><p>Meanwhile, we have to make sure that the new important service is secure by making this service inaccessible from the outside world while also adding additional security features such as key expiry.</p><p>Well, this is a solution! but is this a good one?</p><p>We are creating a centralised system where every service is dependent on it and all the time, which means devastating <strong>single point of failure</strong> and it doesn’t stop there, we are keeping all our shared secrets/keys at one place which is really not a great solution.</p><h4>Decentralised key establishment</h4><p>What does this mean? In simple terms each service pair should be capable of establishing a secret and rotate them whenever it must without human intervention or downtime. Now lets work towards how to achieve the same.</p><h3>Lets decentralise! “key establishment”</h3><p>Now that we have understood that decentralised shared key exchange is a good problem to solve, lets Google “shared key exchange” (Well bing it or use any of your fav search engine).</p><p>The first thing you will notice is one algorithm that is mentioned over and over again i.e. <strong>Diffie Hellman</strong> <strong>algorithm</strong></p><p><strong>Fact</strong>: No it is not created by Diffie or Hellman, it’s theorised by computer scientist and one of public key encryption inventor <strong>Ralph C. Merkle </strong>and named after two famous cryptographers Whitfield <strong>Diffie</strong> and Martin <strong>Hellman</strong>.</p><p>You can find a simple and easy to understand explanation for Diffie Hellman <a href="https://security.stackexchange.com/a/45971/178042">here</a>. I am reiterating the first and important sentence below for quick reference.</p><p>In simple words, Diffie Hellman is a way of <em>generating</em> a shared secret between two people/system in such a way that the secret can’t be seen by observing the communication.</p><blockquote><strong>Important distinction</strong><em>:</em><strong> </strong>You’re <strong><em>not</em> <em>sharing information</em> </strong>during the key exchange, you’re<strong> <em>creating a key</em> together.</strong></blockquote><h4>There is a always a catch!</h4><p>Once we decide to use any new approach for a problem at hand, we tend to wonder what can be the drawbacks of this approach? And yes, there are drawbacks of using DH as it takes lot of resources (i.e. <strong><em>time</em> </strong>an important factor and <em>computing power</em> though computing power is a worry only for small devices in IOT) to create 2048 bit key pair (since <a href="https://wiki.mozilla.org/CA:MD5and1024">1024 bit keys is not safe anymore</a> and <a href="https://github.com/mozilla/server-side-tls/issues/107">Java 6 open source version doesn’t support DH &gt; 1024</a>).</p><p>As always, we have a good alternative — <strong>ECDH</strong>.</p><p>ECDH is better performant than DH and you can find a lot of great resources about it. The philosophy/design behind the DH and ECDH are the same but they achieve results using different algorithms. Elliptic curve cryptography in short <strong>ECC</strong> has provided a great means to improve the performance of many Cryptographic algorithms and one such improvement is ECDH over DH.</p><blockquote>For instance 2048 bits DH key generation in Python 2.7 takes more than 30 seconds and the same takes around 10 seconds on NodeJS while the same level of Security can be achieved by ECDH using a curve NIST P384 which takes less than 10ms to generate public private key value pairs across programming languages.</blockquote><blockquote>(hardware used: Mac Pro 2015 with i5 processor and 8 gigs of RAM).</blockquote><h4>Hey! most of the programming languages already supports ECDH.</h4><p>Yes, A major problem is resolved by the programming languages by providing good cryptographic modules so that you don’t have to worry about implementing DH yourselves. But not all programming languages follow the same interface for e.g. NodeJS crypto methods encodes and expects Public and Private keys in &#39;latin1&#39;, &#39;hex&#39;, or &#39;base64&#39; formats whereas Python and Java works &#39;DER&#39; or &#39;PEM&#39; encoding schemes.</p><p>It doesn’t stop there, now we have to worry about storing this established secret since we can’t afford key exchange for every request.</p><blockquote>Key establishment for each communication session is referred to as <strong><em>perfect forward secrecy</em></strong> for which we have DHE where E stands for ephemeral and being used by most of the your trusted tools such as TLS, SSH, ECDSA etc.</blockquote><p>Since the applications are deployed in a horizontally scaled environment, we need to store the secret in some secure place and we need a way to fetch it for every request which is not solved by these Standard libraries. Hence we are defining a way to generate secrets, store and access the established secrets and expire with TTL (Time to Live) support across heterogenous setups and across multiple programming languages.</p><p>Well it’s not all over yet, we have one drawback with DH/ECDH which is <strong><em>man in the middle attack</em></strong>. Consider you are trying to establish a communication channel between your Service A and and an external Service B, while a prankster Service C poses as Service B in which case you have no way to differentiate the services in an untrusted environment. <strong><em>That is why we are limiting the discussion to Inter-service communication</em></strong> in this article, but there are ways to establish communication with untrusted parties but again over a trusted channel or interacting with trusted third party services.</p><blockquote>ECDH, ECDSA, etc. ECC based DH algorithms are being used by your browser for SSL handshakes, OpenSSL and for most of the secured communication over the Internet.</blockquote><h3>Lets dig into the implementation!</h3><p>Now we have arrived at conclusion that we will be using ECDH for key establishment. But are there any standards? Yes, there are standard curves created for this purpose and you can find these safe curves <a href="https://safecurves.cr.yp.to/">here</a>.</p><ol><li>Use ECDH key exchange for generating a temporary shared secret <strong>valid for a short duration</strong> and storing it in a central datastore that supports expiration.</li><li>Use <strong>Request-In-Request</strong> and not request-response mechanism during the ECDH key exchange for establishing the trust (via Domain validation) for two-way authentication.</li><li>At the end of the ECDH key exchange, services maintain a list of non-secret identifiers of the temporary shared secrets and the expiration timestamp against the service so that they can reuse an existing valid secret if available.</li></ol><h4>I get the Gist but I need more than that.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/695/1*68-OeCLaH2rD7NdVAgC1BQ.png" /><figcaption>Decentralised key establishment approach for Inter-service communication</figcaption></figure><p><strong>Step 1:</strong> Service A generates its ECDH asymmetric key pair and an UUID and store it in a key-value store preferably with support for TTL like DynamoDB or Redis for few seconds (Configurable depending on application preference) with the UUID as the key and the value comprising of the following:</p><ul><li>ECDH curve, Generated private key, Generated public key, Service Info (B in this case)</li></ul><p><strong>Step 2: </strong>Service A makes a direct HTTPS request to Service B with the following details:</p><ul><li>`POST &lt;optional-prefix&gt;/_handshake-1`</li><li>`curve` — ECDH curve used</li><li>`ecdh_pub_key` — Public key generated by service A</li><li>`source service` — Identity of Service A</li><li>`UUID` — Unique identifier for the request</li></ul><p><strong>Step 3: </strong>Service B generates its ECDH asymmetric key pair using the curve provided in the request.</p><p><strong>Step 4: </strong>Service B makes a direct HTTPS request to Service A passing the following details:</p><ul><li>`POST /&lt;optional-prefix&gt;/_handshake-2`</li><li>`pub_key` — Public key generated by service B</li><li>`UUID` — Same UUID provided by Service A</li><li>`source service` — Identity of Service B</li></ul><p><strong>Step 5: </strong>Service A retrieves the data stored initially based on the UUID using a <strong><em>strongly consistent read</em></strong> operation.</p><p><strong>Step 6: </strong>Service A checks that the service passed in the second request matches the value stored in the datastore.</p><p><strong>Step 7: </strong>Service A generates the temporary shared secret by combining the public key of service B and stores it in a data store preferably with <strong><em>TTL support</em></strong> for a pre specified duration with the UUID as the key (overwriting the previous entry that was valid for few seconds).</p><p><strong>Step 8: </strong>Service A responds back to service B’s request with a success response</p><p><strong>Step 9: </strong>On receiving a successful response, service B combines the private key of B and public key of A to generate the temporary shared secret and store it in its data store for a some duration (As per application requirement) with the uuid as the key and the value comprising of the following:</p><ul><li>Temporary shared secret, Service Info (Service A in this case)</li></ul><p><strong>Step 10: </strong>Service B records the uuid and the expiration of the shared secret against the service A for use in the future in its <strong>relational database</strong> with a composite index on (service, expiration) columns.</p><blockquote>Note: In this approach, we are using <strong>relational database</strong> to store service, and expiration info against UUID and using <strong>Key value database with TTL support</strong> to keep the UUID with generated secret which removes single point of failure in terms of Data leak.</blockquote><p><strong>Step 11: </strong>Service B responds with success for the initial request</p><p><strong>Step 12: </strong>On receiving a successful response, service A records the uuid and the expiration of the shared secret against the service B for use in the future in its relational database with a composite index on (service, expiration) columns.</p><h4>Wait! What happens when i have multiple keys and how do i choose one?</h4><ol><li>Use non-secret identifiers (UUID) to communicate which temporary secret was used to sign for the actual communication.</li><li>And while checking the signature, if the shared secret is not found respond with a different error message than when the signature matching fails so that the client can retry after creating a new temporary shared secret.</li></ol><p>And <strong>ta-da</strong> we have created the mechanism to establish key exchange in a decentralised way across services with a mechanism for storing and expiring shared secret key.</p><p>Hope you enjoyed this post. I am ending this article with a quote from <a href="https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle#Maintaining_security">Bruce Schneier</a>:<br>“The fewer and simpler the secrets that one must keep to ensure system security, the easier it is to maintain system security.”</p><h4>Thanking great resources that helped me create this post</h4><ol><li><a href="https://security.stackexchange.com/questions/45963/diffie-hellman-key-exchange-in-plain-english">https://security.stackexchange.com/questions/45963/diffie-hellman-key-exchange-in-plain-english</a></li><li><a href="https://technology.amis.nl/2017/07/04/ssltls-choose-cipher-suite/">https://technology.amis.nl/2017/07/04/ssltls-choose-cipher-suite/</a></li><li><a href="https://www.internetsociety.org/blog/2015/10/diffie-hellman-key-exchange-problems-recommendations-for-stronger-encryption/">https://www.internetsociety.org/blog/2015/10/diffie-hellman-key-exchange-problems-recommendations-for-stronger-encryption/</a></li><li><a href="https://security.stackexchange.com/questions/20803/how-does-ssl-tls-work">https://security.stackexchange.com/questions/20803/how-does-ssl-tls-work</a></li><li><a href="https://crypto.stackexchange.com/questions/12823/ecdsa-vs-ecies-vs-ecdh">https://crypto.stackexchange.com/questions/12823/ecdsa-vs-ecies-vs-ecdh</a></li></ol><p>Special thanks to <a href="https://github.com/justjkk">Kishore Kumar</a> for coming up with this great approach for solving key establishment problem for inter-service communication.</p><p><strong>P.S:</strong> We are working on libraries across programming languages and we are hoping to make it open source as soon as possible.</p><p>Thank you!</p><h4>If you liked this article, please hit the 👏 button to support it. This will help other Medium users find it. <a href="http://twitter.com/share?text=Guys, do checkout the article by @hbksagar on Inter-service authentication and the need for decentralised shared key exchange at the @practodev blog.&amp;url=https://bit.ly/2OGeXVl&amp;hashtags=devops,authentication,auth,RequestInRequest,Decentralised,DiffieHellman">Share this on twitter</a> to help out reach as many readers as possible.</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=29fda3e7e1e0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/inter-service-authentication-and-the-need-for-decentralised-shared-key-exchange-29fda3e7e1e0">Inter-service authentication and the need for decentralised shared key exchange</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Function Overloading in Python]]></title>
            <link>https://medium.com/practo-engineering/function-overloading-in-python-94a8b10d1e08?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/94a8b10d1e08</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[decorators]]></category>
            <category><![CDATA[function-overloading]]></category>
            <category><![CDATA[callable-object]]></category>
            <dc:creator><![CDATA[PAWAN PUNDIR]]></dc:creator>
            <pubDate>Tue, 09 Oct 2018 20:25:11 GMT</pubDate>
            <atom:updated>2018-11-13T15:28:31.457Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently in one of the conversations at Practo, I found some guys complaining that its so bad that we do not have function overloading in Python. This is due to numerous articles and blogs around which claim that Python does not support function overloading. This will be evident the moment you search for function overloading with python.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AORIIwk0Ojs2KFfe3XZ4ew.png" /></figure><p>This was somewhat similar to developers complaining about slower execution speed of Python (addressed in <a href="https://medium.com/practo-engineering/execute-python-code-at-the-speed-of-c-extending-python-93e081b53f04">https://medium.com/practo-engineering/execute-python-code-at-the-speed-of-c-extending-python-93e081b53f04</a>) and some others complaining that Python and other interpreted languages are not truly “concurrent” ( addressed in <a href="https://medium.com/practo-engineering/threading-vs-multiprocessing-in-python-7b57f224eadb">https://medium.com/practo-engineering/threading-vs-multiprocessing-in-python-7b57f224eadb</a>).</p><p>Because of Python being open source and ultra flexible , there is almost nothing which you can achieve with other languages and not with Python.</p><p>Below snippet will highlight the issue of function overloading in Python.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/04775d804dd06cd18c7a809aa1ca01bb/href">https://medium.com/media/04775d804dd06cd18c7a809aa1ca01bb/href</a></iframe><p>So when you define two functions with same names, the last one only matters ( area(size) in above). Thus area(3) works but area(4,5) gives</p><pre>TypeError: area() takes exactly 1 argument (2 given)</pre><p>You can always print <em>locals()</em> symbol table which is a data structure maintained by a compiler containing all necessary information about the program. This stores like below:</p><pre>{‘area’: &lt;function area at 0x102231398&gt;, ‘__builtins__’: &lt;module ‘__builtin__’ (built-in)&gt;,…}</pre><p>In symbol table, key is function name here. Thus when another function with same name is encountered, in symbol table current key value pair is overridden. Hence, by default function overloading is not available.</p><p>But by using decorator design pattern in python, function overloading can be implemented. Below is code snippet to implement function overloading. Explanation will follow later.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/80e9b38c1b6f0f0689424cf874e80a77/href">https://medium.com/media/80e9b38c1b6f0f0689424cf874e80a77/href</a></iframe><p>In above, we have implemented decorator called overload. This is special case of decorators with arguments. These arguments to decorators are the key elements of function overloading since they specify the type and number of arguments.</p><p>Due to the decorator overload, call to area function resolves as below:</p><pre>area (size ) resolves to overload(int)(area)(size)</pre><p>and</p><pre>area (length,breadth) resolves to overload(int, int)(area)(length,breadth)</pre><p>Our main aim is to maintain a typemap dictionary. Here key is types which is a tuple of arguments to decorator and the value is function object.</p><pre>self.typemap[types] = function</pre><p>Let us see typemap in our cases.</p><pre>overload(int)(area)(size)</pre><p>results in :</p><pre>{(&lt;type &#39;int&#39;&gt;,): &lt;function area at 0x102231938&gt;} (actual)</pre><pre>{(int,): &lt;function area at 0x102231938&gt;}(for sake of explanation)</pre><p>and</p><pre>overload(int, int)(area)(length, breadth)</pre><p>results in:</p><pre>{(&lt;type &#39;int&#39;&gt;, &lt;type &#39;int&#39;&gt;): &lt;function area at 0x1022318c0&gt;} (actual)</pre><pre>{(int, int): &lt;function area at 0x1022318c0&gt;} (for sake of explanation)</pre><p>Lets do deep dive to implementation of overload decorator function. From above, its straight to figure out that outer function accepts arguments and inner function accepts function object.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8cf782d39085873f83f71ca2608c1b48/href">https://medium.com/media/8cf782d39085873f83f71ca2608c1b48/href</a></iframe><p>In above code, for area (length,breadth) which resolves to overload(int, int)(area)(length,breadth):</p><p>types = (int,int) and function refers to area function object.</p><p>From function object, we can get name by function.__name__</p><p>mm = MultiMethod(name)</p><p>MultiMethod(name) will now call class MultiMethod with function name (area in our case)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/504ce87a21c077abb5478c50d2daabb0/href">https://medium.com/media/504ce87a21c077abb5478c50d2daabb0/href</a></iframe><p>Thus self.name becomes “area” and self.typemap = {}</p><p>after this decorator calls mm.register(types, function)</p><p>types = (int, int) or (int,) for our cases</p><p>Note: I am using int for (&lt;type ‘int’&gt;) for the sake of easy explanation.</p><p>function refers to function object.</p><p>The register function will generate typemap now as below:</p><pre>{(int,): &lt;function area at 0x102231938&gt; , (int, int): &lt;function area at 0x1022318c0&gt;}</pre><p>The function decorator overload returns register (inner function object) which in turn returns mm object.</p><p>So what happens when actual function call happens:</p><p><strong>Case1:</strong></p><ul><li><em>area (3 )</em> resolves to <em>overload(int)(area)(3)</em></li><li><em>overload(int)(area)</em> returns <em>register</em> which in turn returns <em>mm</em>.</li><li><em>overload(int)(area)(3)</em> thus calls <em>mm(3)</em></li></ul><p>Interesting since mm was an object of MultiMethod class. So calling mm(3) is like calling object with some arguments. But yes, in Python you can have callable objects if class defines __call__ special function.</p><p>Lets see the __call__ function of MultiMethod class in detail.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c713801395405c3a6e37914ff3e8942e/href">https://medium.com/media/c713801395405c3a6e37914ff3e8942e/href</a></iframe><p>when object is called, __call__ method is invoked.</p><p>Thus mm(3) means args = (3,)</p><p>From the passed arguments tuple of the type of variables passed is generated using:</p><pre>types = tuple(arg.__class__ for arg in args)</pre><p>To be noted that __class__ on any object gives its class.</p><p>Example:</p><pre>&gt;&gt;&gt; a  = 34<br>&gt;&gt;&gt; a.__class__<br>&lt;type &#39;int&#39;&gt;<br>&gt;&gt;&gt; a = 45.6<br>&gt;&gt;&gt; a.__class__<br>&lt;type &#39;float&#39;&gt;<br>&gt;&gt;&gt; a = &quot;test&quot;<br>&gt;&gt;&gt; a.__class__<br>&lt;type &#39;str&#39;&gt;</pre><p>Now the invocation is straightforward.</p><pre>function = self.typemap.get(types)</pre><p>since our typemap looks like</p><pre>{(int,): &lt;function area at 0x102231938&gt; , (int, int): &lt;function area at 0x1022318c0&gt;}</pre><ul><li>in case of mm(3) types = (int,)</li></ul><p><em>thus function area at 0x102231938 will be invoked. (area(size))</em></p><p>Case2:</p><p>area(4,5)</p><ul><li><em>area (4,5 )</em> resolves to <em>overload(int,int)(area)(4,5)</em></li><li><em>overload(int,int)(area)</em> returns <em>register</em> which in turn returns <em>mm</em>.</li><li><em>overload(int,int)(area)(4,5)</em> thus calls <em>mm(4,5)</em></li></ul><p>when object mm is called, __call__ method is invoked.</p><p>Thus mm(4,5) means args = (4,5)</p><p>and type becomes (int,int)</p><p><em>Thus &lt;function area at 0x1022318c0&gt; gets called (area(length, breadth)).</em></p><p>Hence, by above strategy we can implement function overloading in Python.</p><p>Finally, actual call happens through</p><pre>function(*args)</pre><p>Between, you can code it once and use everywhere since this is implemented as decorator. So once you have written the code , you never need to write it again, rather you can just use decorator as and when needed.</p><p>You can clone this git repo <a href="https://github.com/ppundir/overload">https://github.com/ppundir/overload</a> and use it to implement decorator in your code without knowing the inside details also.</p><p>Steps:</p><ol><li>git clone <a href="mailto:git@github.com">git@github.com</a>:ppundir/overload.git</li><li>python setup.py install</li></ol><p>usage:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c93f2b53b2d49453a57a5b7adde55f54/href">https://medium.com/media/c93f2b53b2d49453a57a5b7adde55f54/href</a></iframe><h4>If you liked this article, please hit the 👏 button to support it. This will help other Medium users find it. <a href="http://twitter.com/share?text=Hey guys, @hitechpundir released an article on Function Overloading in Python at the @practodev blog.&amp;url=https://bit.ly/2QAR82Z&amp;hashtags=python,functionoverloading,decorator,callableobject">Share this on twitter</a> to help out reach as many readers as possible.</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=94a8b10d1e08" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/function-overloading-in-python-94a8b10d1e08">Function Overloading in Python</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How MVVM helped us run A/B tests quicker]]></title>
            <link>https://medium.com/practo-engineering/how-mvvm-helped-us-run-a-b-tests-quicker-d79397f9067d?source=rss----b91421619e39---4</link>
            <guid isPermaLink="false">https://medium.com/p/d79397f9067d</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[a-b-testing]]></category>
            <category><![CDATA[practo]]></category>
            <category><![CDATA[mvvm]]></category>
            <dc:creator><![CDATA[Dhruv Taneja]]></dc:creator>
            <pubDate>Tue, 09 Oct 2018 19:00:14 GMT</pubDate>
            <atom:updated>2018-10-09T19:09:20.188Z</atom:updated>
            <content:encoded><![CDATA[<p>One of the quickest ways to test your app designs with the least amount of conclusive data is by running A/B tests. At the time when people want to be prudent and factually correct at the same, these tests can turn out to be the go-to thing. For those it is new, A/B test is a test where you put out two(or more) variants of your product, and analyse over time which variant is the winner.</p><p>The main idea for these tests is to build fast and fail fast. With our Practo mobile apps, we always aim to thrive with a data driven approach to optimise user solutions and these tests are the core of this approach now. We use Firebase for our A/B tests for both Android and iOS.</p><h3>What did we need an A/B test for?</h3><p>Before starting the experiment, the <a href="https://www.practo.com">Practo app</a> had <em>four</em> tabs —</p><ol><li>Home tab — The home for all the transactions</li><li>Todo tab — List of all actionables and “todos” for the user</li><li>Read tab — A place to read about health queries and health articles</li><li>Profile tab — Everything about the user</li></ol><p>Since the launch of Todo cards, we have a lot of faith in the product and the data has been equally corroborating. So we wanted to offer it to the users right on the first screen, like this —</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*ObF_ts5_IsSs9fPF9Uo81Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*ebe6rdg9osLLbGwDEN2aHg.png" /><figcaption>Home without todo cards(left) and Home with todo cards(right)</figcaption></figure><p>We had an assumption that putting todo cards on home can give over the top success for the product, and equally happy users. So we decided to run an A/B test to concretise the decision.</p><h3>How MVVM actually helped</h3><p>As mentioned above, the main idea of A/B tests is to build fast and fail fast. Which means put the code there fast, fix the bugs fast, and make sure the features don’t break in any of the variants. For a fair test, you would not want any disparity of your feature support between your release and patch cycles.</p><p>For the brevity of this post, I will not try to explain some of the best practices that should be followed for writing MVVM, but I will advocate how it can accelerate both business <em>and</em> development.</p><p>For one of the variants, todo cards is a separate tab on our home screen. So we have a fragment, and a viewmodel for the todo tab on home, i.e., TodoHomeViewModel. The fragment is nothing but a container for a recycler view sugar coated with some cool UI. And the responsibility of the view model is to create a list of todo cards that the fragment can display. The viewmodel has a bunch of observables and livedata for databinding, and recycler adapter’s data. But the main sorcery happens within these functions -</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/866a8459de278b1b890594b024be0d43/href">https://medium.com/media/866a8459de278b1b890594b024be0d43/href</a></iframe><p>The fragment subscribes to the <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Maybe.html">Maybe</a> returned by the viewmodel, and gets the todo cards from the server. The viewmodel then creates different viewmodels for different cards. The observable list is updated with these viewmodels. And the list is observed to spit the cards in the recyclerview. Everything is working. Unit tests are running. Life is good!</p><p>For convenience, the AppHomeViewModel(the viewmodel for home) is also structured in a similar way. There’s an observable list being observed by the app home fragment, and functions that put data into that list.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ad47af7e38007e8b2a2c7f817d0b3e4a/href">https://medium.com/media/ad47af7e38007e8b2a2c7f817d0b3e4a/href</a></iframe><p>Now comes the second variant, where the todo cards are on the home tab and the todo fragment does not exist. So how can MVVM help here? With this implementation that we have here with a bunch of observables and a list being observed, putting the results at one place hardly appears to be a puzzle now. All we have to do now is to instantiate the TodoHomeViewModel in the AppHomeViewModel, make it fetch the cards from the server just the way todo fragment does, observe the response and append the cards to the list.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2f56abbec5afafb1a503a220d8c1025f/href">https://medium.com/media/2f56abbec5afafb1a503a220d8c1025f/href</a></iframe><p>And the updated tests —</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/33c0e445fdf5445cd878576a4966f526/href">https://medium.com/media/33c0e445fdf5445cd878576a4966f526/href</a></iframe><p>Everything is working. Unit tests are running. Life is good!</p><blockquote>Most importantly, time taken to get the A/B experiment out on production = Time taken to instantiate the todo home viewmodel in the app home viewmodel + Time taken to observe the cards sent by the server + Time taken to append those cards to the list of segments. A big time saver for the business. A win-win for all.</blockquote><p>And to put this all in picture —</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/447/1*JnC5cCxlwE9wHnngJZAONQ.png" /></figure><h3>Why MVVM</h3><p>Well you can still live with copying and pasting the same code for two different variants of the app (I would like to argue against that though). Or put everything in huge methods and wrap everything in a lot of if’s and else’s and things would still work. But things can get, and will get complicated when you have more than 2 variants, or multiple tests running at the same time. The business logic will get lost in the bulk of control flow logic and code maintenance, scalability and debugging will go for a toss. And you will find yourself praying to stop any more variants or tests coming in your code. After adding a few more variants on top of that, it will start looking like jenga blocks about to fall.</p><p>MVVM framework helps in maintaining <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">single responsibility principle</a> and helps in writing easy communications between different components via <a href="https://en.wikipedia.org/wiki/Observer_pattern">observer objects</a>, <a href="https://en.wikipedia.org/wiki/Mediator_pattern">mediator objects</a> or some other behaviour that suits the case.</p><p>To wrap things up, investing in MVVM gives us some cogent advantages:</p><ol><li>Zero code duplication — No need to rewrite blocks of business logic again and again.</li><li>Separation of concern — Individual features, or view models have the responsibility of (only) their own experiments.</li><li>Maintainability and scalability — The code will look a lot less daunting and more breathable. Adding more experiments will not take much time. (Remember: build fast, fail fast)</li><li>Maintains testability — This is by far the favourite gains we have had from writing in MVVM involving multiple experiments. There are little to no changes in the unit test cases for each individual view models. Since, a lot of our experiments were UI-centric, our unit test suite remained almost the same. So along with building fast and failing fast, we could also maintain the sanctity of our code.</li></ol><p>Writing or rewriting your code in MVVM might feel like a needless or a redundant task at first, but it really pays you in the future. It does not take much time getting used to, and the gains are surely worth it, to — <em>Build fast, fail fast.</em></p><h4>If you liked this article, please hit the 👏 button to support it. This will help other Medium users find it. <a href="https://twitter.com/home?status=Practo%20Engg%20%40practodev%20just%20released%20an%20awesome%20article%20on%20usage%20of%20%23MVVM%20for%20A/B%20Test%20from%20an%20%23app%20standpoint%20written%20by%20Dhruv%20Taneja%20%40dhruvtan3ja.%20%23Practo%20%23Android%20https%3A//medium.com/practo-engineering/how-mvvm-helped-us-run-a-b-tests-quicker-d79397f9067d%20">Share this on Twitter</a> to help out reach as many readers as possible.</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d79397f9067d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/practo-engineering/how-mvvm-helped-us-run-a-b-tests-quicker-d79397f9067d">How MVVM helped us run A/B tests quicker</a> was originally published in <a href="https://medium.com/practo-engineering">Practo Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>