<?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[Stories by Harsh Bhikadia on Medium]]></title>
        <description><![CDATA[Stories by Harsh Bhikadia on Medium]]></description>
        <link>https://medium.com/@daadu?source=rss-49de32e2100------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*mXVDtTY5ZqhMWngwt5GGJA.jpeg</url>
            <title>Stories by Harsh Bhikadia on Medium</title>
            <link>https://medium.com/@daadu?source=rss-49de32e2100------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 15 Apr 2026 04:42:10 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@daadu/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[MQTT with Python Web Server like a Charm]]></title>
            <link>https://medium.com/@daadu/mqtt-with-python-web-server-like-a-charm-9425dec116ef?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/9425dec116ef</guid>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[mqtt]]></category>
            <category><![CDATA[backend]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Fri, 27 Dec 2024 19:37:03 GMT</pubDate>
            <atom:updated>2024-12-27T19:37:03.287Z</atom:updated>
            <content:encoded><![CDATA[<h4>A tale of making Eclipse Paho work on a production IoT project.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-t3zsxGDccnoZDI0QqrZ6g.jpeg" /><figcaption>DALL-E: Python snake with a postal message envelope in its</figcaption></figure><blockquote><strong>Disclaimer: </strong>The article was written in October 2021, but was only published in December 2024. Therefore some of the information might be outdated</blockquote><p>MQTT is a popular choice of protocol, especially for IoT systems. It is a perfect for connecting constraint embedded devices to the cloud because of its light footprint, publish-subscribe model, bi-directional connection, and support to secure connection with TLS.</p><p>In general, an embedded device connects to an MQTT broker to publish data — mostly sensor readings and subscribes to a topic to listen for commands from the cloud. The user’s web or app client can send these commands directly to the broker, or the server (on behalf of the user) could send it. This post covers the latter method in detail; it is an essential piece of the system, especially when you want non-MQTT clients (like OAuth application) to access the IoT-enabled device.</p><pre>                       ┌──────────────────────────────┐<br>                       │                        CLOUD │<br>            ┌──────┐   │  ┌──────┐     ┌────────────┐ │<br>            │DEVICE│◄──┼─►│      │     │            │ │<br>            │  A   │   │  │      │     │            │ │<br>            └──────┘   │  │      │     │            │ │<br>            ┌──────┐   │  │      │     │            │ │<br>            │DEVICE│◄──┼─►│ MQTT |◄───►| WEB SERVER │ │<br>            │  B   │   │  │BROKER│     │            │ │<br>            └──────┘   │  │      │     │            │ │<br>            ┌──────┐   │  │      │     │            │ │<br>            │DEVICE│◄──┼─►│      │     │            │ │<br>            │  C   │   │  │      │     │            │ │<br>            └──────┘   │  └───▲──┘     └──────▲─────┘ │<br>                       └──────┼───────────────┼───────┘<br>                              │               │<br>                          ┌───▼───────────────▼───┐<br>                          │         USER          │<br>                          ├──────┬──────┬─────────┤<br>                          │MOBILE│ WEB  │3rd PARTY│<br>                          │CLIENT│CLIENT│OAUTH APP│<br>                          └──────┴──────┴─────────┘</pre><p><em>Since our cloud application is in Python, all the implementation details mentioned here are for it, but the same approaches could be taken in other languages/stacks. Also, this post is limited to MQTT v3.1 and v3.1.1.</em></p><h3>A little background</h3><p>As mentioned above, we had a similar setup in our IoT project — devices connecting to MQTT broker and users (via mobile apps) connecting to our web server. We wanted to send “commands” to the devices from the web server. The commands could either — be fire-and-forget or request-response types, executed in-process (of web request) or by the background workers, sent to one or multiple devices at a time.</p><p>Considering those use-cases, we were able to jot down the core utilities required for it:</p><ul><li>a simple blocking function to publish any message</li><li>a simple blocking function to subscribe to topics, wait for <em>n</em> number of messages or until timeout, returning the messages received</li><li>a simple blocking function to achieve request-response kind of functionality — could be a wrapper of the above two</li></ul><p>After that, we started looking for an MQTT client to connect the webserver to the broker. We decided to go with paho-mqtt — the python module part of the <a href="https://www.eclipse.org/paho/">Eclipse Paho</a> project, which also maintains MQTT libraries for other popular languages. The module is stable, feature-rich as well as well designed. However, it took us some time and iteration to work with it.</p><p>This article goes through that journey of making MQTT work with our web server like a charm — it puts forward various iterations, learnings, and know-hows we had during a year with this project.</p><p>I write this post as a series of actions, events, and decisions to give a complete picture. If you want some code snippets to work with, you can find the link to the code snippet below.</p><p><a href="https://gitlab.com/-/snippets/2183967">MQTT helper functions and classes in Python. ($2183967) · Snippets · Snippets</a></p><h3>[v1] Simple wrapper functions</h3><p>We thought this would be easy — as paho mostly did the heavy lifting and had the blocking APIs we needed(well, almost, you will see!).</p><p>Publish function was simple. It was a “wrapper” around the <a href="https://www.eclipse.org/paho/index.php?page=clients/python/docs/index.php#single">paho.mqtt.publish.single</a> function.</p><pre>def publish_to_mqtt(<br>        topic: str, payload: str, qos: int = 0, retain: bool = False<br>) -&gt; MQTTMessageInfo:<br>    return publish.single(<br>        ...<br>    )</pre><p>Similarly, we found <a href="https://www.eclipse.org/paho/index.php?page=clients/python/docs/index.php#simple">paho.mqtt.subscribe.simple</a>, but this one was not enough. It did not have any “timeout”, meaning it will block forever if no message on that topic is received. We decided to copy the code of it and add a check for timeout (the bold part below). That did the job.</p><pre>def subscribe_to_mqtt(<br>        topics: Union[str, List[str]],<br>        qos: int = 0,<br>        msg_count: int = 1,<br>        retained: bool = False,<br>        timeout: int = 5,<br>        userdata: dict = None<br>) -&gt; Union[MQTTMessage, List[MQTTMessage]]:<br>    <em>&quot;&quot;&quot;<br>    modified paho.mqtt.subscribe.simple() method to support timeout<br>    &quot;&quot;&quot;<br><br>    </em># callback for on_connect<br>    def _on_connect(..., userdata: dict, ...):<br>        # raise Exception if did not connect<br>        ...<br>        # subscribe to topics in userdata<br>        ...<br>        <br><br>    # callback for on_message<br>    def _on_message_simple(..., userdata: dict, message: MQTTMessage):<br>        # ignore if msg_count already 0<br>        ...<br>        # ignore if message is &quot;retained&quot; and user does not want it<br>        ...<br>        # decrement msg_count and set/add it to messages in userdata<br>        ...<br>      <br><br>    # validate msg_count and qos<br>    ...<br>    # Set ourselves up to return a single message if msg_count == 1, or a list if &gt; 1<br>    ...<br>    # make userdata<br>    ...<br>    # create client instance and set callbacks and user credentials<br>    ...<br>    # connect to broker, mark start_time and start &quot;threaded network loop&quot; with client<br>    ...<br>    <strong># wait till all message received or timeout<br>    while True:<br>        should_timeout = time.time() &gt; start_time + timeout<br>        if userdata[&quot;msg_count&quot;] == 0 or should_timeout:<br>            client.loop_stop()<br>            client.disconnect()<br>            break<br>        time.sleep(0.2)</strong><br>    <strong># raise error if no or less messages found<br>    messages = userdata[&quot;messages&quot;]<br>    if not messages or len(messages) &lt; msg_count:<br>        raise TimeoutError(...)<br></strong>    return messages</pre><p>We naively thought that combining the above two functions would do the job for request-response. But that did not work very well, as client instances were created and connected twice, making it really slow and practically useless — as most of the time, our devices will send responses before the server even subscribes.</p><p>So instead of using the publish_to_mqtt function, we modified the above subscribe_to_mqtt such that the “publish arguments” are passed as userdata. When the client is connected, it publishes this data after subscribing. This optimization made sure that the publish happens in the same connection as subscribe, and also subscription is done before publishing so that the response is not lost.</p><pre>def subscribe_to_mqtt(...):<br>    ...<em><br>    </em># callback for on_connect<br>    def _on_connect(client: MQTTClient, userdata: dict, flags: int, rc: int):<br>        ...<br>        # subscribe to topics in userdata<br>        ...<br>        <strong># publish data based on pub_on_connect in userdata<br>        if &quot;pub_on_connect&quot; in userdata:<br>        ...</strong></pre><pre>def request_response_to_mqtt(<br>        topic: str,<br>        payload: str,<br>        response_topic: str,<br>        qos: int = 0,<br>        response_msg_count: int = 1,<br>        response_timeout: int = 5,<br>        retained: bool = False<br>) -&gt; Union[MQTTMessage, List[MQTTMessage]]:<br>    <strong># create userdata with `pub_on_connect`<br>    userdata = {<br>        &quot;pub_on_connect&quot;: {<br>            &quot;topic&quot;: topic, &quot;payload&quot;: payload, &quot;qos&quot;: qos<br>        }<br>    }</strong><br>    # subscribe with created userdata and<br>    # wait for messages on response_topic<br>    return subscribe_to_mqtt(<br>        <strong>userdata=userdata,</strong><br>        ...<br>    )</pre><p>You can find the full code for this under <a href="https://gitlab.com/-/snippets/2183967">mqtt_v1.py file in the code snippet</a>.</p><pre>&gt;&gt;&gt; # simple demo<br>&gt;&gt;&gt; for i in range(5):<br>...     start=time.time(); rpc(device, &quot;RPC.Ping&quot;); end=time.time()<br>...     print(f&quot;[{i}] Time taken: {end - start}&quot;)<br>... <br><br><strong>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;</strong><br>[0] Time taken: 1.220142126083374<br><strong>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;</strong><br>[1] Time taken: 0.8168301582336426<br><strong>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;</strong><br>[2] Time taken: 1.0094208717346191<br><strong>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;</strong><br>[3] Time taken: 1.0069520473480225<br><strong>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;</strong><br>[4] Time taken: 1.003695011138916</pre><p>And that’s it. We were up and running as it passed the basic tests, and that was enough until our users started complaining about latency — more about that in the next section.</p><h3>[v2] Singleton that re-uses the connection</h3><p>Once after rolling out the system in beta with about 20–25 devices and its users, we got some feedback from them. They were experiencing noticeable latency (more than 3 seconds) when running commands to control their devices.</p><p>After doing some basic profiling, it was clear that the bulk of the latency is because of the TLS handshake and connection establishment with the broker. Each time the sever interacts with the broker, it has to establish a new TCP+TLS connection. We decided to optimize this in the same way Database connections in web servers usually do — persisiting the connection and re-using it. Since most webservers (at least the WSGI once) re-use the processes created for serving web requests, it is possible to have an MQTT connection that can outlive the HTTP request-response cycle.</p><p>To accomplish this, we decided to use a Singleton class that holds the client and its connection. This class would then expose those utilities and under the hood use the persisted connection. Most of the work was to re-organize the existing code from the previous version to support this pattern. The singleton class (we called it MQTTAgent) looked something like the below.</p><pre>class MQTTAgent:<br>    _instance: &quot;MQTTAgent&quot; = None<br>    <strong>_client: Optional[MQTTClient] = None</strong><br><br>    @staticmethod<br>    def get_instance() -&gt; &quot;MQTTAgent&quot;:<br>        <em>&quot;&quot;&quot; Static access method. &quot;&quot;&quot;<br>        ...</em></pre><pre>    def __init__(self):<br>        <em>&quot;&quot;&quot; Virtually private constructor. &quot;&quot;&quot;<br>        ...</em><br><br>    def clean_up(self):<br>        <em>&quot;&quot;&quot; Cleanup network connection and client. &quot;&quot;&quot;<br>        ...</em><br><br>    def _connect_async(self):<br>        <em>&quot;&quot;&quot; Setup client and connection asynchronously. &quot;&quot;&quot;<br>        ...</em><br><br>    def connect(self, timeout: int = 5):<br>        <em>&quot;&quot;&quot; Connect synchronously with timeout. &quot;&quot;&quot;<br>        ...</em><br><br>    def disconnect(self):<br>        <em>&quot;&quot;&quot; Disconnect from broker synchronously. &quot;&quot;&quot;<br>        ...</em><br><br>    def __on_connect(self,<br>                 client: MQTTClient,<br>                 userdata: dict,<br>                 flags: int,<br>                 rc: int):<br>        <em>&quot;&quot;&quot; Callback called when client connects. &quot;&quot;<br>        ...<br></em><br><br>    def __on_message(self,<br>                 client: MQTTClient,<br>                 userdata: dict,<br>                 message: MQTTMessage):<br>        <em>&quot;&quot;&quot; Callback called when message received. &quot;&quot;&quot;<br>        ...</em><br><br>    <strong>def is_connected(self) -&gt; bool:</strong><br>        <em>&quot;&quot;&quot; Check if connected to broker. &quot;&quot;&quot;<br>        ...</em><br><br>    <strong>def publish(self,<br>            topic: str,<br>            payload: str,<br>            qos: int = 1,<br>            retain: bool = False) -&gt; MQTTMessageInfo:</strong><br>        <em>&quot;&quot;&quot; Publish message synchronously. &quot;&quot;&quot;<br>        ...</em><br><br>    <strong>def subscribe(self,<br>              topics: _Topics,<br>              qos: int = 1,<br>              retained: bool = False,<br>              msg_count: int = 1,<br>              timeout: int = 5) -&gt; _Messages:</strong><em><br>        ...</em><br><br>    <strong>def request_response(self,<br>                     topic: str,<br>                     payload: str,<br>                     response_topics: _Topics,<br>                     qos: int = 1,<br>                     response_msg_count: int = 1,<br>                     response_timeout: int = 5,<br>                     retained=False) -&gt; _Messages:</strong><em><br>        ...</em><br><br><br># convenient global instance that could be<br># directly imported and used.<br><strong>mqtt_agent = MQTTAgent.get_instance()</strong></pre><p>You can find the full code for this under <a href="https://gitlab.com/-/snippets/2183967">mqtt_v2.py file in the code snippet</a>.</p><pre>&gt;&gt;&gt; # simple demo<br>&gt;&gt;&gt; for i in range(5):<br>...     start=time.time(); device.rpc(&quot;RPC.Ping&quot;); end=time.time()<br>...     print(f&quot;[{i}] Time taken: {end - start}&quot;)<br>... </pre><pre><strong>Connection Accepted.[0] mqtt client connected to mqtt-broker:1883.</strong><br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br><strong>[0] Time taken: 1.0129196643829346</strong><br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br><strong>[1] Time taken: 0.10076355934143066</strong><br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br><strong>[2] Time taken: 0.10155487060546875</strong><br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br><strong>[3] Time taken: 0.10111713409423828</strong><br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br><strong>[4] Time taken: 0.1007685661315918</strong></pre><p>The MQTT utility is now clean and elegant to use as well as efficient. The latency improved by a factor of 10 (check the demo code output above). Users noticed the difference and appreciated it. A win for the day!</p><h3>[v3] Making it thread-safe</h3><p>When we got the requirement to make concurrent command calls to multiple devices, we had two options — execute them in various threads or use a distributed task queue system (we already had a <a href="https://github.com/celery/celery">Celery</a> setup with our server).</p><p>The former looked like the apt solution but would require us to make the v2 solution “thread-safe”, as mqtt_agent.subscribe(and therefore mqtt_agent.request_response) was not designed keeping concurrency in mind. Instead of doing that, we lazily went with the “task queue” approach. It would schedule the concurrent command calls and hands them to distributed background workers for execution. It did the job, but because the tasks were “queued” and “distributed-ly executed”, the performance was not consistent. It would take more than expected time — when the queue is long, or workers are busy. It often happened as the number of devices per user increased. It looked like we had no other option but to make MQTTAgent thread-safe.</p><p>In v2, the userdata(set as an attribute of MQTTClient and passed to on_message handler) keeps the “subscription state”; the key to making MQTTAgent thread-safe is to make it thread-safe. The idea is to have a “store” of all “subscription states”, and when a message is received, it checks the store, and if any “subscription” had requested to receive messages from that topic, then we add this message to that “subscription state”. We also need to handle subscriptions to the topics smartly — if two thread subscribes to the same topic then internally it should only subscribe once and give the same message to both.</p><p>Firstly, we created _MQTTSubscription class for storing “subscription state” instead of using dict. Also moved the logic of adding new messages inside the class.</p><pre><strong>class _MQTTSubscription:</strong><br>    <strong>def __init__(self,<br>                 topics: _Topics,<br>                 msg_count: int = 1,<br>                 qos: int = 1,<br>                 retained: bool = False):</strong><br>        self.thread_id: int = current_thread().ident<br>        <strong>self.topics: List[str] = topics if isinstance(topics, list) else [topics]</strong><br>        <strong>self.messages: _Messages = list() if msg_count &gt; 1 else None</strong><br>        <strong>self.pending_msg_count: int = msg_count</strong><br>        self._msg_count: int = msg_count<br>        self.qos: int = qos<br>        self.retained: bool = retained<br><br>    <strong>def add_message(self, message: MQTTMessage):<br>        <em>&quot;&quot;&quot; Handle new message for this subscription. &quot;&quot;&quot;</em></strong><em><br>        </em># ignore - pending_msg_count &lt;= 0 or message is retained but the request is not for retained<br>        ...<br>        # decrement pending_msg_count and add it to messages<br>        ...</pre><p>After that, we added the “subscription store” and “topic reference count” attributes to MQTTAgent. The “subscription store” is a set of all subscription instances that are waiting for messages. The “topic reference count” is a map of the topic and the number of subscription instances subscribed to it.</p><pre>class MQTTAgent:<br>    ...<br>    <strong>_subscriptions: Set[_MQTTSubscription] = set()<br>    _topics_count: Dict[str, int] = dict()</strong><br><br>    ...<br>    <br>    def _disconnect(self)<br>        ...<br>        <strong># reset _subscriptions, _topic_count<br>        self._subscriptions = set()<br>        self._topics_count = dict()</strong><br>        ...</pre><p>When a new “subscription” is requested, a subscription instance is created and added to _subscriptions set, “topic reference count” is incremented for those topics, the _client subscribes to the topics that are not already subscribed (with reference count as 1). The reverse happens when the subscription needs to be “closed or unsubscribed”, the reference count is decremented, and the _client unsubscribes to the topics that are not referenced anymore (with reference count as 0). In __on_message callback, we check all _subscriptions and call subscription.add_message for the once where the subscription topic and message topic match.</p><pre>class MQTTAgent:<br>    ...<br><br>    def __on_message(self, ...):<br>        <em>...<br>        </em><strong># iterate through subscriptions and find any that has this topic<br>        for subscription in tuple(self._subscriptions):<br>            ...<br>            # check if any topic matches for this subscription<br>            ...<br>            # add message to subscription<br>            subscription.add_message(message=message)</strong><br><br>        ...<br>    ...</pre><pre><strong>def _subscribe(self, subscription: _MQTTSubscription):<br>        <em>&quot;&quot;&quot; Subscribe based on subscription. &quot;&quot;&quot;<br>        </em># put it in _subscriptions<br>        ...<br>        # increment topic count - make list of topics to subscribe (once that are not yet subscribed)<br>        ...<br>        # subscribe to those topics<br>        ...</strong><br><br>    <strong>def _unsubscribe(self, subscription: _MQTTSubscription):<br>        <em>&quot;&quot;&quot; Unsubscribe based on subscription. &quot;&quot;&quot;<br>        </em># pop subscription from _subscriptions<br>        ...<br>        # decrement topic count - make list of topics to unsubscribe (once that are not needed)<br>        ...<br>        # unsubscribe to those topics<br>        ...</strong><br><br>    def subscribe(self, ...) -&gt; _Messages:<br>        ...<em><br>        </em># connect to client<br>        ...<br>        # validate  msg_count and qos<br>        ...<br>        <strong># make subscription object<br>        subscription = _MQTTSubscription(<br>             topics=topics,<br>             msg_count=msg_count,<br>             qos=qos,<br>             retained=retained<br>        )<br>        # subscribe<br>        self._subscribe(subscription=subscription)</strong><br>        # wait till all message received or timeout<br>        start_time = time.time()<br>        while True:<br>            should_timeout = time.time() &gt; start_time + timeout<br>            if <strong>subscription.pending_msg_count==0</strong> or should_timeout:<br>                <strong># unsubscribe<br>                self._unsubscribe(subscription=subscription)</strong><br>                break<br>            # sleep<br>            time.sleep(0.1)<br>        <strong>return subscription.messages<br>   </strong>...</pre><p>You can find the full code for this under <a href="https://gitlab.com/-/snippets/2183967">mqtt_v3.py file in the code snippet</a>.</p><pre>&gt;&gt;&gt; # simple demo<br>&gt;&gt;&gt; print(f&quot;Calling RPC to {len(devices)} device concurrently.&quot;)<br><strong>Calling RPC to 5 device concurrently.</strong><br>&gt;&gt;&gt; <strong>with ThreadPoolExecutor(len(devices)) as executor:</strong><br>...     start = time.time()<br>...     # make sure mqtt_agent is connected<br>...     mqtt_agent.connect() <br>...     tuple(executor.map(<br>...         lambda device: rpc(device, &quot;RPC.Ping&quot;),<br>...         devices<br>...     ))<br>...     end = time.time()<br>...     print(f&quot;Total time taken: {end - start}&quot;)<br>... <br>Connection Accepted.[0] mqtt client connected to mqtt-broker:1883.<br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br>[140217424803584] Time taken: 0.14078974723815918<br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br>[140217441588992] Time taken: 0.1967012882232666<br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br>[140217433196288] Time taken: 0.19601941108703613<br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br>[140217416410880] Time taken: 0.19498801231384277<br>RPC: RPC.Ping responses: b&#39;{&quot;result&quot;: &quot;pong&quot;}&#39;<br>[140217449981696] Time taken: 0.6972782611846924<br><strong>Total time taken: 1.314225673675537</strong></pre><p>It was now possible to call MQTTAgent functionalities from multiple threads safely. We started calling commands to multiple-device concurrently with the help of Python <a href="https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor">ThreadPoolExecutor</a>(as shown in the above demo code) instead of distributing it to background workers. It has not just made it faster and efficient, but we could now scale it to make simultaneous calls to a higher number of devices (tested with 100 devices at a time).</p><h3>What next?</h3><p>We have some ideas that could take the utility to the next level. Some of them are as follows.</p><ul><li>“Iter/Stream” like API for getting messages on subscription as they are received. Currently, all messages are “collected” and then returned.</li><li>“Future/Promise” like API for having both async and sync options at the user level. Currently, all functionality is blocking.</li><li>Multiple Broker support.</li></ul><p>The current implementation is enough for our use cases, so we might not work on it. But if we get good responses to this post, we could think of working on it and releasing it as an open-source python package. While <a href="https://www.eclipse.org/paho/index.php?page=clients/python/docs/index.php">Eclipse Paho</a> is solid and stable, it requires an “elegance layer” on top of it to make it work like a charm, like how the<a href="https://docs.python-requests.org/en/latest/">requests</a> package does it for <a href="https://github.com/urllib3/urllib3">urllib3</a>. This package could exactly be that — MQTT for Humans™!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9425dec116ef" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Stages of Hardware Product Development, and How to Navigate Them.]]></title>
            <link>https://medium.com/@daadu/stages-of-hardware-product-development-and-how-to-navigate-them-78c7ad40b89f?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/78c7ad40b89f</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[manufacturing]]></category>
            <category><![CDATA[product-development]]></category>
            <category><![CDATA[hardware]]></category>
            <category><![CDATA[hardware-startup]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Fri, 17 Nov 2023 14:42:53 GMT</pubDate>
            <atom:updated>2023-11-17T14:44:43.991Z</atom:updated>
            <content:encoded><![CDATA[<p><em>I decode various stages of hardware product development and the intricacies at each stage.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ya3UVU0Q6Ao0tAMq.jpeg" /></figure><p>In the initial stages of developing the <a href="https://nexthome.co/">SmartBoard at Next Home</a>, decisions were like navigating through the fog-unclear and shrouded in uncertainty for the future. Some choices felt right at the moment but left us questioning their viability in the long run, and vice versa. Connecting with other hardware enthusiasts in the <a href="https://github.com/hardwaremafia">Hardware Mafia</a> community and our customers at <a href="https://makenica.com">Makenica</a> showed me that our challenges were shared.</p><p>Challenges like this inspired me to write about the intricacies of hardware startup struggles. Building on my previous article, <a href="https://bhikadia.com/posts/the-hard-things-about-building-a-hardware-startup">The Hard Things About Building a Hardware Startup</a>, let’s now explore the details of various stages of product development.</p><p><a href="https://bhikadia.com/posts/the-hard-things-about-building-a-hardware-startup">The Hard Things About Building a Hardware Startup | Harsh Bhikadia</a></p><p>Every stage in hardware development comes with its unique set of demands. Transitioning from one phase to the next requires not just a change in gears but a fundamental shift in mindset. Although a lot of aspects of these stages are shared with “pure software” products, I will be looking at them in terms of a hardware product.</p><h3>Proof of Concept (PoC) Stage</h3><blockquote><em>This is the phase when you have an idea and want to quickly test it.</em></blockquote><ul><li>Be as scrappy as possible — build with whatever you got</li><li>Don’t take too long to build; this is not the stage where you want to spend too much time</li><li>Always start with mockups rather than building the actual thing</li><li>Be honest to yourself, in terms of validating the idea</li><li>Don’t fixate on the product or solution yet; experiment with various technologies, form-factor, etc.</li></ul><h3>Prototyping Stage</h3><blockquote><em>Here, you iterate and move closer to your product.</em></blockquote><ul><li>Start showcasing to your potential customers, and let their insights shape product features.</li><li>Understand the problem space better, and build deeper insights regarding it</li><li>Optimize for iteration time and not unit cost, at this stage you are developing and not trying to make a profit</li><li>Maintain the specification of the product that you have in mind, each iteration should solidifying it</li><li>Each iteration should be a step towards the ultimate product</li></ul><h3>Pilot Stage</h3><blockquote><em>At this stage, you think you have a product that can sell.</em></blockquote><ul><li>Match production-grade parameters as much as possible-in terms of quality, cost, performance, etc</li><li>Mitigate failure cases, and minimize risks associated with it</li><li>Have product quality checks and standards in place</li><li>Validate your product in real-world scenarios</li><li>Meet customer expectations, as this phase is a critical bridge between development and full-scale production</li><li>Finalize Bill of Material (BoM), manufacturing processes, material, etc</li></ul><h3>Production Stage</h3><blockquote><em>At this point, you are producing replicas as fast and cheaply as possible.</em></blockquote><ul><li>Optimize Cost of goods sold (COGS), reduce the number of custom parts, assembly time, etc. go for anything that can reduce the cost without comprising quality</li><li>Plan for contingencies; ensure adaptability to diverse suppliers and components.</li><li>Have contracts and deals in place to secure better prices and ensure stable supplies.</li><li>Build solid partnerships with various manufacturers and suppliers for your product</li><li>Minimize failure rates during the manufacturing process.</li></ul><p>Developing hardware products is like a journey with different stages, each telling its own story. Priorities and challenges change, requiring a constant dance of adaptation. Remember, success comes from moving forward in the development process, not getting stuck at any one stage for longer than is required. The ability to make correct decisions regarding it is what separates the better product builders.</p><p><em>Originally published at </em><a href="https://bhikadia.com/posts/stages-of-hardware-product-development-and-how-to-navigate-them"><em>https://bhikadia.com</em></a><em>.</em></p><p><a href="https://bhikadia.com">Harsh Bhikadia</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=78c7ad40b89f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[New Personal Website]]></title>
            <link>https://medium.com/@daadu/new-personal-website-7a73543b4188?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a73543b4188</guid>
            <category><![CDATA[personal-web-site]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[website]]></category>
            <category><![CDATA[storytelling]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Tue, 24 Oct 2023 19:52:10 GMT</pubDate>
            <atom:updated>2023-10-24T19:52:10.572Z</atom:updated>
            <content:encoded><![CDATA[<h4><em>Meta post about the new personal website, probably the one you are viewing!</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rcIXb3YZavEAaJTG.png" /></figure><p>A few months back I wrote about <a href="https://bhikadia.com/posts/what-is-a-personal-website">What is a personal website?</a>, while I was pondering on the point of having a personal website. Basically I threw the old inactive website and used my Medium profile as my website’s landing page. From then on I managed to write more articles, averaging 1 every month.</p><p><a href="https://bhikadia.com/posts/what-is-a-personal-website">What is a personal website? | Harsh Bhikadia</a></p><p>I have been also re-skilling myself with modern web development, The last time I did web was way back in 2015! Primarily doing it as an effort to revamp the <a href="https://makenica.com/">Makenica</a> website. To put my skill to the test I decided on having a new website again. Apart from that I am also taking this opportunity as a way to express myself, not getting restricted to what Medium had to offer. So, keep an eye on custom MDX components I drop in various blogs!</p><h3>The Stack</h3><p>I picked up the <a href="https://lexingtonthemes.com/info/profoliox/">Profoliox</a> template, that came with the bundle I purchased from <a href="https://lexingtonthemes.com/">LEXINGTON</a>. I was really impressed with the look and feel of the template — quite minimalist but at the same time also expandable. The template is built on <a href="https://astro.build/">Astro</a> and <a href="https://tailwindcss.com/">Tailwind CSS</a>, excellent choices for building a static site that is composed of “Components”.</p><p>That was about the developer stack, for editing I will be using <a href="https://obsidian.md/">Obsidian</a>. It is something with which I am quite familiar with. I really like the simplicity it brings when it comes to writing and reading <a href="https://daringfireball.net/projects/markdown/">Markdown</a> files. You can expand the basic functionality with various excellent <a href="https://obsidian.md/plugins">plugins</a> developed by the community.</p><p>The whole stack is meant to perform really fast on the user’s browser, shipping minimum javascript, along with excellent editing experience for the editor.</p><h3>The Hacking</h3><p>After finalising the stack, it was time to start building it. Starting from a professional template saved me a lot of time. The UI is almost untouched by the template.</p><p>After the basic setup was done, I wrote the content for the <a href="https://bhikadia.com/">Home</a> and <a href="https://bhikadia.com/work">Work</a> pages. Once that was done, it was time to start importing the articles I published on <a href="https://medium.com/@daadu">my Medium</a>. This is what took the maximum time. In the process, I had to learn about MDX and had to build a component for embedding links inside the article (check the top of the article, I have embedded the link of an old post).</p><p>Few touches and finalisation here and there, and it was done. In all it took me a weekend to complete the website.</p><p>I really like the website — the way it looks, and the technology behind it. Also thought would be apt to write about it as well, and hence this post. I have a few more ideas that I would like to incorporate in future.</p><p><em>Originally published at </em><a href="https://bhikadia.com/posts/new-personal-website"><em>bhikadia.com</em></a><em>.</em></p><p><a href="https://bhikadia.com">Harsh Bhikadia</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a73543b4188" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Avoid chasing mirages, Talk to your potential Manufacturers as early as possible.]]></title>
            <link>https://medium.com/@daadu/avoid-chasing-mirages-talk-to-your-potential-manufacturers-as-early-as-possible-9b172bd856a3?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/9b172bd856a3</guid>
            <category><![CDATA[manufacturers]]></category>
            <category><![CDATA[hardware]]></category>
            <category><![CDATA[manufacturing]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[hardware-startup]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Sun, 13 Aug 2023 06:20:21 GMT</pubDate>
            <atom:updated>2023-08-13T06:20:21.423Z</atom:updated>
            <content:encoded><![CDATA[<p>Once upon a time in the city of <em>Technagar</em>, a young and ambitious entrepreneur named Jay had a brilliant idea. He envisioned a sleek and fancy device that would revolutionise the way people interacted with technology. Filled with excitement, Jay decided to pursue it.</p><p>He followed what <a href="https://theleanstartup.com/">Eric Ries of ‘The Lean Startup’ fame</a> said about talking to potential customers to validate his idea. He discovered that his product addressed a significant pain point, and there exists no solution for it either. Encouraged by the positive feedback, he started putting hours in developing the product. He also created captivating renders to showcase its potential. He got overwhelming response, customers where eager to get their hands on that device.</p><p>Months after working on it, when he started to delve deeper into the manufacturing process, reality hit him hard. He realised that his designs are not manufacturable at commercially viable cost. He had fallen into the trap that countless entrepreneurs face: the lack of early engagement with manufacturers. It is probably as important as talking to customers, but is often overlooked by a lot of Hardware entrepreneurs.</p><h3>How would it help?</h3><p>Engaging with manufacturers early in the product development process provides a multitude of benefits that can save entrepreneurs from costly mistakes and ensure a smoother journey towards success.</p><h4>Help you from taking any “dead end” paths</h4><p>Manufacturers possess a wealth of knowledge and experience. They have witnessed countless product development cycles and are familiar with the pitfalls and mirages that often trap enthusiastic entrepreneurs. By involving manufacturers from the outset, they can help identify potential roadblocks, technological limitations, and design constraints, allowing you to navigate around them.</p><h4>Get to know the viability of your product soon</h4><p>Manufacturers can offer invaluable insights into the feasibility of your product. They can evaluate the proposed design and determine whether it can be efficiently manufactured within the desired timeframe and budget. Their expertise enables them to identify any potential manufacturing challenges early on, enabling you to make informed decisions about product modifications or pivots before investing significant resources.</p><h4>Get an estimate of the cost in producing the goods</h4><p>One of the critical factors in any product’s success is its cost-effectiveness. Manufacturers can provide accurate cost estimates based on their knowledge of materials, production processes, and economies of scale. By understanding the production costs upfront, entrepreneurs can make informed decisions about pricing, profitability, and the overall market viability of their product.</p><h3>Why people don’t do it?</h3><p>Despite the numerous benefits, entrepreneurs often hesitate to engage with manufacturers early in their product development journey due to a few common misconceptions.</p><h4>Fear of manufacturers stealing their idea</h4><p>Entrepreneurs may fear that sharing their idea with manufacturers exposes them to the risk of intellectual property theft. While it is essential to protect your intellectual property rights, engaging with manufacturers does not necessarily entail revealing the full details of your invention. By signing non-disclosure agreements (NDAs) and carefully selecting trusted manufacturers, entrepreneurs can mitigate the risk while still benefiting from their expertise.</p><h4>Belief that it’s not the right time to talk to manufacturers</h4><p>Some entrepreneurs mistakenly believe that engaging with manufacturers should only occur once their product design is finalized or closer to production. However, involving manufacturers early can prevent costly redesigns or rework later in the process. Even if you haven’t committed to a particular manufacturer, seeking their advice and insights can help shape your design decisions and ensure manufacturability from the start.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qt8rB8S4YHgSYKLKCRU7Dw.jpeg" /></figure><p>At <a href="https://makenica.com">Makenica</a>, we have witnessed numerous startups and founders embark on their product development journeys, we strongly advocate for early engagement with manufacturers. It will be super valuable. Their expertise can guide you towards a more viable and manufacturable product, saving you time, money, and headaches along the way. So, take the leap and start engaging with those who can help turn your product dreams into reality. Happy Making!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9b172bd856a3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Value — The Real Currency of Entrepreneurship]]></title>
            <link>https://medium.com/@daadu/value-the-real-currency-of-entrepreneurship-40586c6e04a9?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/40586c6e04a9</guid>
            <category><![CDATA[value-creation]]></category>
            <category><![CDATA[value-proposition]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[startup-lessons]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Wed, 09 Aug 2023 01:35:46 GMT</pubDate>
            <atom:updated>2023-08-09T02:12:23.877Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Value: The Real Currency of Entrepreneurship</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yGCDKIN6Wmkz0Co2" /><figcaption>Photo by <a href="https://unsplash.com/@mathieustern?utm_source=medium&amp;utm_medium=referral">Mathieu Stern</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Hello🙏, readers! This blog is going to discuss the concept of “Value”. How you as an entrepreneur (or any professional) should consider it as the real measure of your work instead of anything else.</p><p>Often entrepreneurs measure success by the amount of business they’re doing. But for me, it is just a side-effect of something more profound — Value.</p><p>When I say “Value,” I’m not talking about vague paper valuations or mystical calculations. I’m referring to the value proposition your product or service brings to the table. It’s about your role in the grand scheme of things — the market, the supply chain, the lives of your customers. In simple words, it’s about how darn useful your offering is.</p><p>Think about it this way: your product or service should be so packed with “value” that it’s on par (or even more) with the cash it’s exchanged for. We’re getting into some deep economics waters here, and if you’re curious, you can venture into the vast realm of the <a href="https://en.wikipedia.org/wiki/Theory_of_value_(economics)">Theory of Value</a><a href="https://en.wikipedia.org/wiki/Theory_of_value_(economics)).">.</a></p><p>But hey, let’s keep it real. I’m no economics guru, and I’m guessing you’re not looking for a lecture either! Instead, I’m talking from my eight years of experience running multiple startups. Trust me, I’ve seen the real magic happen when you focus on delivering value rather than just chasing numbers.</p><p>I would want to share a couple of concepts around the idea of Value, that has stayed in my head. They are also good signals for indicating that you’re generating Value or holding a key role in the Value chain:</p><h4>“Shut Up and Take My Money” Moments</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*i3yEynsUXsFPtMu6.jpg" /><figcaption>“Shut up and take my money!” meme —Read more on <a href="https://knowyourmeme.com/memes/shut-up-and-take-my-money">KnowYourMeme</a></figcaption></figure><p>Have you seen the above meme before? Well, it’s more than just a meme; it’s a profound expression of value. Think about those products or services that are so darn valuable that when you’re pitching them, your potential customers are practically throwing money at you. It’s that moment when they can’t wait to get their hands on what you’re offering because they see its immense worth. I think this meme aptly represents the idea of <a href="https://www.ycombinator.com/library?categories=Product%20Market%20Fit">Product-Market Fit</a> as well.</p><h4>The Double “Thank You” Moments</h4><p>Something that I picked up from <a href="https://www.amitvarma.com/">Amit Varma</a>’s podcast — “<a href="https://seenunseen.in/">The Seen and the Unseen</a>”, <a href="https://web.archive.org/web/20181104125912/https://abcnews.go.com/2020/story?id=3231572">originally stated by John Stossel</a>. Imagine a transaction where both you and your customer walk away thanking each other. It’s not just about the product changing hands; it’s about mutual appreciation for the value exchanged. You’ve probably experienced this when you’ve finally found that perfect item you’ve been searching for, and the seller genuinely thanks you for choosing their store. “Win-win”, “poisitive-sum” are other ways of explaining the same phenomena.</p><p>So, dear entrepreneurs, let’s shift our focus from merely doing business to creating value that resonates. Remember, when your product or service is so incredibly valuable that people can’t help but thank you (and themselves) for it, that’s when you know you have made it as an entrepreneur.</p><p>Stay valuable, and stay awesome! 🚀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=40586c6e04a9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[pip chill not freeze!]]></title>
            <link>https://medium.com/@daadu/pip-chill-not-freeze-5116ffbce111?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/5116ffbce111</guid>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[dependencies]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[package-manager]]></category>
            <category><![CDATA[dependency-management]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Tue, 25 Jul 2023 06:32:04 GMT</pubDate>
            <atom:updated>2023-07-25T07:15:17.492Z</atom:updated>
            <content:encoded><![CDATA[<h4>Just add direct dependencies to requirements.txt, automatically.</h4><p>🙏Namaste Pythonistas! Let me ask you something — how do you usually maintain your requirements.txt file? If you’re like me, you must be using good ol’ pip freeze &gt; requirements.txt command. Sure, it’s quick and easy, but have you ever found yourself looking at the file later and wondering what on earth cffi==1.15.1 refers to? Have you struggled to figure out if it was installed directly or as a dependency of another package? The ideal way would be to “hand write” it, but who has the time for it!?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C8F9rB-DHdH7Ja35F4rXNg.png" /><figcaption><a href="https://labs.openai.com/">DALL-E</a> generated — photo of python frozen in ice.</figcaption></figure><p>Well, let me tell you about <a href="https://pypi.org/project/pip-chill/">pip-chill</a>. It’s like a smarter version of pip freeze. Instead of simply dumping all installed packages into the requirements file, it shows only the ones you directly installed.</p><p>To get started, just install pip-chill using pip itself:</p><pre>pip install pip-chill</pre><p>After that here is how I generate the requirements.txt file now:</p><pre>pip-chill --no-chill -v &gt; requirements.txt</pre><ul><li>--no-chill : does not include pip-chill itself in the output</li><li>-v : outputs the indirect dependencies as well, but commented. Therefore it is easy to keep track of them.</li></ul><p>It generates the following for a basic Django project.</p><pre>django==4.2.3<br>psycopg2-binary==2.9.6<br>requests==2.31.0<br># asgiref==3.7.2 # Installed as dependency for django<br># certifi==2023.5.7 # Installed as dependency for requests<br># charset-normalizer==3.2.0 # Installed as dependency for requests<br># idna==3.4 # Installed as dependency for requests<br># sqlparse==0.4.4 # Installed as dependency for django<br># typing-extensions==4.7.1 # Installed as dependency for asgiref<br># urllib3==2.0.4 # Installed as dependency for requests<br># urllib3==1.26.16 # Installed as dependency for botocore, requests</pre><p>See how pretty it looks. It just works and solves my problem of properly maintaining requirements.txt. Now, I can focus on coding and building cool stuff, leaving the hassle of maintaining the requirements file to pip-chill.</p><blockquote><em>So just, </em>take a <strong>chill</strong> pill with <a href="https://pypi.org/project/pip-chill/">pip-</a><a href="https://pypi.org/project/pip-chill/"><strong>chill</strong></a> and <strong>chill</strong>ax!</blockquote><blockquote><em>Happy coding 👋.</em></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5116ffbce111" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Hard Things About Building a Hardware Startup]]></title>
            <link>https://blog.startupstash.com/the-hard-things-about-building-a-hardware-startup-6abec6a211dd?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/6abec6a211dd</guid>
            <category><![CDATA[manufacturing]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[hardware-startup]]></category>
            <category><![CDATA[startup-lessons]]></category>
            <category><![CDATA[startup-storytelling]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Sun, 02 Jul 2023 04:37:16 GMT</pubDate>
            <atom:updated>2023-07-06T09:28:28.590Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8mtEYb7Sg11AsWRX" /><figcaption>Photo by <a href="https://unsplash.com/@nyancrimew?utm_source=medium&amp;utm_medium=referral">maia crimew</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>We have been building our consumer hardware startup for the past 4–5 years. Currently, it is in the “pre-production” phase where we are waiting for our first production batch to come from manufacturers. When we started it, we massively underestimated the amount of effort and time it will take to just develop the product.</p><p>Our previous venture before this was an app tech business which we managed to scale to more than 1 million registered users in a matter of 1 year. The app was launched within a month after the ideation. And all of this was done with 4 people team out of an apartment.</p><p>When we decided to do hardware, we thought the journey would be similar — will develop our MVP in a few months, launch it and then build a pipeline of products around the same problem space. We thought that the fundamentals of any startup are the same — building products that customers need. While the statement remains true, the “stuff” you are dealing with is completely different. Therefore the blueprint we had, had to go out of the window. We had to adjust, adapt and learn on the job while doing it.</p><p>While we were building it we had pondered multiple times about — How is it different from a regular software-tech startup? How is it similar? Why do people keep saying it is hard? What challenges, if solved would reduce the parity in building a hardware startup?</p><p>Now that we have reached so far with our startup, and thinking about these things — I have compiled a list of things that make building a Hardware startup harder.</p><h3>1. Iterations are costly</h3><p>Developing any product requires iterations. You need to iterate a lot, especially if you are building something that has never been built before. In general, the cost of iterating each time is many-fold higher than just doing software.</p><p>Take “build time” for example — it is the time taken to generate an “artefact” from an abstract design — it is usually PCB from its design, a plastic part from CAD or drawing file and binary from source code. While for the latter it is just a matter of a few minutes. For both the former once, it could take weeks if not months to get your designs in hand.</p><p>Not just cost, a short iteration cycle also reduces the risk associated with developing a new product by a huge factor. It makes reverting from a wrong product decision much easier.</p><h3>2. Can’t fix things on the fly</h3><p>With software, things can be fixed over the air, it works so well and is so cost-effective (practically no cost) that it is taken for granted and is not much appreciated by that industry.</p><p>While on the other hand, the best case for any hardware issue is fixing it on the field or calling back devices for rework, the worst could be building new units from scratch. All of this costs money, and it badly demotivates people working on it. They start doubting themselves, the product, etc — while such issues are just part and parcel of developing a product — no new product is without its issue.</p><p>Fulfilling product guarantees is what kills most companies that reach that stage.</p><h3>3. Dealing with physical things</h3><p>Physical products come with physical limitations or problems. Things like delivering products without damaging, stocking or producing them in the right batch size, keeping a dust-free environment, having the right toolsets, etc.</p><p>Imagine you negotiated a small paid pilot of 20–25 units of your product, and you started building them, while screwing you snap your screwdriver multiple times causing scratch marks on the plastic part, each time that happens you dissemble and assemble it back with a new part, finally, you manage to complete the batch and ship it. It reaches the customer, he opens the box and 3 devices are broken and 2 are malfunctioning — although you did thorough testing before shipping.</p><p>This has happened to us and many other startups who took these things lightly.</p><h3>4. Scaling is not just replicating more</h3><p>From 1 unit to even 100 — you will have to re-write your designs, and change processes, components and vendors multiple times. That is how it is. There is no auto-scaling or server-less equivalent here that will work at all scales.</p><p>Also, you cannot take shortcuts and skip any steps in scaling, doing that would be fatalistic. You have to build first build 1 working unit to be able to make 10 and from there to 100s. Until you reach a stable point of production, you will have to re-configure and re-tool your process each time.</p><h3>5. Multi-disciplinary problems</h3><p>Most hardware startups today can be summarised as “custom software written for custom electronics, wrapped in custom plastic”. They are almost always multi-disciplinary. Although such projects are a lot of fun to work on, it also comes with its problems.</p><p>To start with assembling such a team is hard. Then once you have it, the teams usually tussle with each other to mark their territories — to decide what feature or spec is whose responsibility. If not managed well this could become toxic and derail the project itself.</p><p>And good luck with debugging “multi-disciplinary issues”! It is not usually obvious where the issue lies. Reproducing the issue to debug on first place is harder.</p><h3>6. Lacking ecosystem</h3><p>Lastly, all of the above could be less painful if a support system for such a startup existed. No startup is 100% built on its own, it requires ecosystem players, partners and enablers to work for them, in the case of hardware you need it locally too. Along with that access to people who have “been there, done that” is also required. Such a community is non-existing in India — everyone is building in silos.</p><p>For example take China(<a href="https://www.youtube.com/watch?v=SGJ5cZnoodY&amp;pp=ygUSc2NoZW56ZW4gaGFyZHdhcmUg">particularly Schenzen</a>), today if you want to build a small accessory product (a plugin or extension equivalent of software), you can build, test, succeed or fail fast in that ecosystem. That is why you see “funky gadgets” coming from there. It is a good sign. It indicates there is room for anyone to come up with any wild idea and at least deliver a minimal viable product for it.</p><p>Without such an ecosystem, people will take safe bets — build “me too” products for which the market already exists — because they know doing anything different would be like swimming against the tide.</p><p>Noticing a vacuum for an enabler in the ecosystem that can empower hardware startups and pave their way for success, we also started <a href="https://makenica.com">Makenica.com</a>. With it, we want to revolutionise how innovative hardware ideas come to life.</p><p>I firmly believe that while hardware presents unique challenges, it is certainly within reach. It also requires its own playbook, it can’t be done in the same way as an app or SaaS product.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6abec6a211dd" width="1" height="1" alt=""><hr><p><a href="https://blog.startupstash.com/the-hard-things-about-building-a-hardware-startup-6abec6a211dd">The Hard Things About Building a Hardware Startup</a> was originally published in <a href="https://blog.startupstash.com">Startup Stash</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What is a personal website?]]></title>
            <link>https://medium.com/@daadu/what-is-a-personal-website-f13fd537fff?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/f13fd537fff</guid>
            <category><![CDATA[fear]]></category>
            <category><![CDATA[personal]]></category>
            <category><![CDATA[personal-website]]></category>
            <category><![CDATA[essay]]></category>
            <category><![CDATA[newness]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Sun, 04 Jun 2023 04:46:43 GMT</pubDate>
            <atom:updated>2023-07-30T09:22:11.913Z</atom:updated>
            <content:encoded><![CDATA[<p>I just changed my website. Earlier was hosted with GitHub Pages and now it just redirects to the Medium profile page. That made me ponder — What is a personal website? What is it supposed to do? What should be in it?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6VT5s-JSD7doxHl3" /><figcaption>Photo by <a href="https://unsplash.com/@timmossholder?utm_source=medium&amp;utm_medium=referral">Tim Mossholder</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>A trip down memory lane</h3><p>The earlier site I mentioned was set up during my college days. Back in 2015, it was my final year and it was college placements season. Although I was never truly interested in taking a placement from college or a job in general, my parents wanted me to sit for it. “This can be your backup if you change your mind” — they said.</p><p>So, the first task of the “placements” is to make and submit your resume, but I went a step further and decided to set up a website. I always wanted to have a site, don’t know why! I couldn’t resist when I saw that the domain bhikadia.com (that&#39;s my surname) was available. I always wanted to give my non-Gmail email to people and here I had <em>harsh@bhikadia.com</em> and in my head, it was the coolest thing I owned. Long story short — I picked a template (went with <a href="https://github.com/jekyllt/sustain">Sustain</a>) — rewrote my resume in Markdown— and published it.</p><p>I graduated and never took any job, continued with my startup. The website remained as it is. The email address I was excited about — I never checked the inbox of it. The only update to the website I did was to remove my address and phone number, when I got a call from a random guy seeking guidance on career, he mentioned he got my number from the site.</p><p>So apart from accidentally breaching my privacy, the website was useless to me and I never worked on it from then.</p><h3>Back to present</h3><p>Now, the bug of <em>having-a-cool-website</em> has bitten me again! This time I wanted to start clean, so I decided to just have the Medium profile. This time equally unclear about what to do with it — came the genesis of this <em>very meta</em> article.</p><h4>So, <em>Why am I doing it?</em></h4><ul><li>Without sounding narcissistic, I realized I do like talking about my work and things I am interested in with people in my private circle. But why do I hesitate to do the same in public?</li><li>I <em>fear</em> people’s judgment and what to fight that <em>rakshasa</em> within. I want to make this website — the weapon against it.</li><li>For a year or so — I have been writing in my private journal and started to like it. I think some of the ideas and writings would interest people in general.</li></ul><p>To conclude, I am still determining where this (writing blogs) will take me. This might just wither like has happened in past, or for a change I make it bloom this time around.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f13fd537fff" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From SSH-ing to Cloud Native]]></title>
            <link>https://medium.com/@daadu/from-ssh-ing-to-cloud-native-39011d8e35b2?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/39011d8e35b2</guid>
            <category><![CDATA[helm]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[cloud-native]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Mon, 26 Sep 2022 04:31:30 GMT</pubDate>
            <atom:updated>2022-09-26T12:57:55.209Z</atom:updated>
            <content:encoded><![CDATA[<h4>My (highly-opinionated) step-wise guide on how to do it.</h4><p>Before 2019, all my projects were traditionally deployed on VMs — managed manually by SSH-ing into them — replicated using images or snapshotting features provided by the cloud provider. While that was enough when the projects were small, and updates were not often, some tasks were difficult or impossible to do with that setup — e.g., upgrading OS, migrating across cloud providers, automatic scaling, etc. On the other hand, I hosted projects on PaaS platforms like Google App Engine and Heroku — which had an excellent one-command deployment process. But these platforms had many limitations in covering many use cases and were tightly coupled with cloud providers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/430/1*Se4nG7zjIRG27pNxFiJidQ.png" /></figure><p>Meanwhile, I have followed developments around Containerization, Kubernetes, etc. I saw these technologies as providing PaaS like experience on IaaS platforms. But back then, it seemed very overwhelming — unable to conceptualize the complete picture and unsure of where to start, especially for my brown-field projects. I took a step-by-step approach towards it — figuring out what technology or tool to choose from at each step. It took about 2–3 months to migrate fully. This article is about that journey — it outlines a blueprint for anyone wanting to do the same. I have tried to keep it agnostic to any framework, stack, or cloud provider. To keep it concise, I have relied on links to other good detailed resources — encourage you to go through them.</p><h3>0. Prepare your application</h3><p>Before starting anything, you should prepare your application to be a good citizen of Cloud Native land. You need to familiarize yourself with <a href="https://12factor.net/">“The Twelve-Factor App”</a> — a methodology for how to write Software-as-a-Service applications and deploy them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/602/1*AnmJya4TtHlrNk8ZcKPcDQ.png" /></figure><p>Most good backend frameworks follow some or all principles mentioned. If the framework you use does not cover some, you will find good plugins/packages/libraries to work around it. In most cases, you are just a <a href="https://www.google.com/search?q=%3Cframework-name%3E%2012%20factor%20app">google search</a> away from knowing about it.</p><p>These are generally good to follow principles, irrespective of Cloud Native. So, your efforts will not go in vain, even if you don’t go all the way.</p><h3>1. Containerize your application</h3><p>The first step towards this journey is to containerize your application — package it with all its required dependencies — so that it can run as an isolated “container” independent of any environment or host OS. Containerization (to run “container”) is similar to Virtualization (to run “VM”). But the abstraction is at different levels — it does not include the entire OS — therefore, the size and start time are significantly smaller.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4tOB-sTFnb3YhVH6l9DMCg.png" /></figure><p>Docker is the de-facto containerization tool to build, run and remotely store images. The following are some excellent resources that will help you in it:</p><ul><li><a href="https://docs.docker.com/get-started/">Docker Official Getting Started Guide</a></li><li><a href="https://www.freecodecamp.org/news/what-is-docker-used-for-a-docker-container-tutorial-for-beginners/">What is Docker Used For? A Docker Container Tutorial for Beginners</a></li><li><a href="https://www.freecodecamp.org/news/the-docker-handbook/">The Docker Handbook</a></li><li><a href="https://bluelight.co/blog/how-to-choose-a-container-registry">How to Choose a Container Registry: The Top 9 Picks</a></li><li><a href="https://devopscube.com/reduce-docker-image-size/">How to Reduce Docker Image Size: 6 Optimization Methods</a></li><li><a href="https://geekflare.com/securing-docker-for-production/">How to Secure Docker for Production Environment?</a></li></ul><p>For any framework-specific guide, a simple <a href="https://www.google.com/search?q=%3Cframework-name%3E+dockerize">google search</a> should help you out.</p><h3>2. Write scripts for each workflow step</h3><p>You will need to write scripts, command line tools, aliases, etc., to streamline all or most developer processes — like running static analysis, building applications, tests, etc. These will also come in handy when you write CI/CD pipelines in the future. You could also integrate these scripts with the IDE your team uses.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*UWCxs_8NG_ZaGATW-OpqPw.jpeg" /></figure><p>You can start with converting your README guide to an executable script that could bootstrap the dev environment for you. An automated, streamlined and repeatable process is the key to having a pleasant onboarding for developers.</p><p>You could also use the <a href="https://docs.docker.com/compose/">docker-compose tool</a> to spin up all services — your application with its dependent services like DB, Caching, etc. with one command. It allows you to write your multi-container stack as a YAML file. It can be used in production as well, for simple use cases.</p><h3>3. Get comfortable with k8s</h3><p><a href="https://kubernetes.io/">Kubernetes</a> (aka k8s) is the de-facto platform to run containerized applications in the cloud at scale. It is impossible to cover all k8s concepts here, but I will skim through various key ones.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*BbrbBu7KiY09r9RiptRRhg.png" /></figure><p>At its core, it is a controller and an orchestrator that tries to match the running state with the desired state. The desired state is<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/"> provided in the form of YAML files</a>. If you have gone through anything related to k8s, you might have come across them. It, in general, has the following format with required fields:</p><pre>apiVersion: v1<br>kind: &lt;resource kind&gt;<br>metadata:<br>  name: &lt;resource name&gt;<br>  labels:<br>    &lt;arbitrary key&gt;: &lt;arbitrary value&gt;<br>spec:<br>  &lt;spec based on resource kind&gt;</pre><p>The unit of compute in k8s is the <a href="https://kubernetes.io/docs/concepts/workloads/pods/">Pod</a> — a group of one or more tightly-coupled containers that share network and storage. Pods are rarely created on their own but are instead deployed using various workload resource types — based on the use case. These are some of the workload types:</p><ul><li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Deployment</a> — to keep replicas of pods running and do rolling updates of them</li><li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/">StatefulSet</a> — manage “stateful” applications that require <a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/">persistence</a></li><li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/">DaemonSet</a> — to your pods on each Node of the cluster</li><li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/">Job</a> — to run pods until it “completes”</li><li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/">CronJob</a> — to run pods periodically</li></ul><p>Along with it, to <a href="https://kubernetes.io/docs/concepts/configuration/overview/">manage configuration for the Pods</a>, use <a href="https://kubernetes.io/docs/concepts/configuration/configmap/">ConfigMap</a> and <a href="https://kubernetes.io/docs/concepts/configuration/secret/">Secret</a> resources.</p><p>These Pods are ephemeral. Therefore addressing them with IP to communicate with each other is impractical. Here, K8s’ <a href="https://kubernetes.io/docs/concepts/services-networking/service/">Service</a> resource comes to the rescue — it exposes (privately or publicly) a set of Pods as a single DNS name.</p><p>All K8s workloads are <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/">namespaced</a>; this allows the isolation of resources within a cluster. You can deploy the same resources in different namespaces — e.g., to deploy staging and production in the same cluster.</p><p>As of now, try and stay away from running “stateful” applications inside the k8s — as it is a bit advanced. I suggest only going that route once you have been running a stable cluster for a while. Use managed services if offered or if you decide to self-host it — then do it in a standalone VM outside of k8s.</p><p>For development, to run k8s on your desktop, use <a href="https://minikube.sigs.k8s.io/docs/">minikube</a>, <a href="https://microk8s.io/">MicroK8s</a>, <a href="https://docs.docker.com/desktop/kubernetes/">Docker For Desktop</a>. Although they are good to start with, they are not full-fledged, and I would suggest using a cloud-based k8s cluster to get real experience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/621/1*q3Z3NQ9PU4bPbX1o8dSIFA.jpeg" /></figure><p>Various cloud providers have managed k8s offering, <a href="https://medium.com/geekculture/state-of-managed-kubernetes-2021-43e8a4ca0207">here</a> is a comparison guide. As Kubernetes is <a href="https://www.cncf.io/certification/software-conformance/">standardized</a>, that makes it also agnostic to the provider. Therefore migrating or replicating clusters across these vendors is seamless (at least for stateless applications), making your application free of any vendor lock-ins.</p><h3>4. Improve your k8s experience</h3><p>Now that you have <a href="https://kubernetes.io/docs/reference/kubectl/kubectl/">kubectl</a> apply some YAML files to a cluster, it’s time to make this process less painful and less prone to typos!</p><p>I suggest using the following tools to manage a k8s cluster:</p><ul><li><a href="https://k9scli.io/">k9s</a> — It is an excellent command line GUI that makes k8s monitoring and managing a cakewalk. There is no need to write long commands or be precise with pod names. It is feature-rich, and I highly recommend switching to it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o-mQDJj8qRXLq-jyIHWOKQ.png" /></figure><ul><li><a href="https://helm.sh/">Helm</a> — It is the de facto package manager for your k8s cluster. Essentially it is a template engine for k8s resources — simplifying deployments with no or less configuration. Along with that, it also manages the install-update-uninstall cycle of these resources. Use <a href="https://artifacthub.io/">ArtifactHub</a>, to find out various open source or community-managed Helm charts for services like — PostgreSQL, Redis, RabbitMQ, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/794/1*vur4tX3j_WnW1uf6gN3NHg.jpeg" /></figure><p>Some more tips:</p><ul><li>Use the kubectl port-forward (or k9s) to access admin/internal services without exposing it to the outer world.</li><li>Write a Helm chart for your application. This way, your workloads are packaged and can be configured for different environments. It also makes installing staging and production with the same workloads but differing configs (like DB name, hostname, etc.). The chart generated by the <a href="https://helm.sh/docs/helm/helm_create/">helm create</a> command is a good starting point.</li></ul><h3>5. Expose your service to the Internet</h3><p>While you could directly expose your K8s service with the type <a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer">LoadBalancer</a>, the preferred way is via <a href="https://kubernetes.io/docs/concepts/services-networking/ingress/">Ingress</a> resource and an <a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/">IngressController</a> installed in the cluster. With it, you can use a single LoadBalancer (saving cost) for multiple services instead of using one for each service.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*Hf5sQwZW_cqxBK1o4ZHPRA.png" /></figure><p>An Ingress resource defines the HTTP(S) route from outside the cluster to the service within. It describes various ingress rules based on parameters like domain, path prefix, TLS certificates, etc.</p><p>An Ingress resource does nothing on itself; your cluster requires an IngressContoller installed to make it work. In most cases, <a href="https://kubernetes.github.io/ingress-nginx/deploy/">ingress-nginx</a> will meet your requirements, but there are <a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers">many options here</a>. I found <a href="https://medium.com/flant-com/comparing-ingress-controllers-for-kubernetes-9b397483b46b">this comprehensive comparison</a> [<a href="https://docs.google.com/spreadsheets/d/1DnsHtdHbxjvHmxvlu7VhzWcWgLAn_Mc5L1WlhLDA__k/edit#gid=0">sheet</a>] beneficial.</p><h3>6. Git repo for your k8s cluster</h3><p>Now that you have familiarized yourself with k8s and understand how it works, it’s time to “write” and version-control your k8s cluster that you will deploy in production. This practice is called Infrastructure-as-Code (IaC), allowing you to use other source code tools like Git, Pull Request, CI, etc., with your infrastructure. We need one or more tools to achieve this, to read the code and figure out what workloads should be spun up, updated, or torn down.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*F6Sp1-4Wt0-vFAh-CASJvg.jpeg" /></figure><p>Since Helm releases are the unit of our deployment, I suggest using <a href="https://github.com/helmfile/helmfile">Helmfile</a> as the primary tool for managing them. It allows you to “write” Helm releases, have a different config for each environment, and provides convenient commands like — helmfile apply and helmfile sync. Although not a full-fledged IaC tool, i.e., it can not increase or decrease a node in a cluster but is enough to take care of our k8s needs. For that, you can use <a href="https://www.terraform.io/">Terraform</a>, but I found it overkill for my requirements.</p><p>Since all our configurations will be in a Git repo, we must manage secrets properly. <a href="https://helmfile.readthedocs.io/en/latest/#environment-secrets">Helmfile does this</a> for you along with <a href="https://github.com/jkroepke/helm-secrets">helm-secrets</a> plugin — it encrypts these secrets so that it is safe to put them in a repo.</p><p>The file structure of my repo looks like the following:</p><pre>local/<br>  values.yaml<br>  secrets.yaml.dec<br>staging/<br>  values.yaml<br>  secrets.yaml<br>production/<br>  values.yaml<br>  secrets.yaml<br>values/<br>  my-app.yaml<br>  rabbitmq.yaml<br>  redis.yaml.gotmpl<br>  ...<br>helmfile.yaml<br>values.yaml<br>secrets.yaml<br>.sops.yaml</pre><h3>7. Install some must-have k8s utilities</h3><p>Now, as most things are ready, some of the following must-have utilities will come in handy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/353/0*JHczVZTN-b_4DRvm" /></figure><ul><li><a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack">prometheus-stack</a> — It is the monitoring, alerting, and observability stack for your k8s cluster. It installs <a href="https://prometheus.io/">Prometheus</a> and <a href="https://grafana.com/grafana/">Grafana</a> to the cluster and starts collecting vitals of all Pods right out of the box. Encourage you to learn more about it and use it to monitor your services.</li><li><a href="https://cert-manager.io/">cert-manager</a> — It will automatically manage the issuance and renewal of all your TLS certificates. It works with all issuing mechanisms or providers — <a href="https://cert-manager.io/docs/configuration/selfsigned/">self-signed</a>, <a href="https://cert-manager.io/docs/configuration/ca/">CA based</a>, <a href="https://cert-manager.io/docs/configuration/acme/">ACME server based</a> (like <a href="https://letsencrypt.org/">Let’s Encrypt</a>), and <a href="https://cert-manager.io/docs/configuration/external/">various thers</a>.</li><li><a href="https://github.com/kubernetes-sigs/external-dns">ExternalDNS</a> — It will synchronize all K8s services and ingress with DNS providers. It works with <a href="https://github.com/kubernetes-sigs/external-dns#status-of-providers">various DNS providers</a>.</li></ul><h3>8. CI/CD plumbing</h3><p>Now let’s start triggering various builds and deployments by pushing them to our git repo. You must<a href="https://www.altexsoft.com/blog/engineering/cicd-tools-comparison/"> pick one</a> of the CI/CD servers to run these jobs. I usually use <a href="https://docs.gitlab.com/ee/ci/">GitLab CI</a> as I host my git repo there.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*NnAdHhctiliRSa2NhkhyOg.png" /></figure><p>One of the prerequisites of doing CI is to have a good automated test suite. You can execute these steps manually if you are not doing it or are not confident with it. However, I encourage you to start moving towards having a reliable automated test suite.</p><p>I configure my pipeline in the following way:</p><ul><li>Continuous Integration — This pipeline will execute tasks like static checks, unit tests, docker build, and push. Every commit push to any branch will trigger it.</li><li>Continuous Deployment — This pipeline will execute all CI tasks, along with version bumps, push Helm chart, automatically deploy (with Helmfile) to staging and wait for manual approval for production deployment. Every tag push will trigger it.</li></ul><p>These pipeline configurations are not universal. Feel free to be imaginative with it. Ultimately it is an automated process to streamline the integration of software and its deployment.</p><h3>9. Continue improving</h3><p>Congratulations! You should be able to deploy your production application and various dependent services in a Cloud Native way. While this may be enough, you have a long way to go when it comes to it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*m7tPmWLv5ektmEJ40cusXw.jpeg" /></figure><p>I encourage you to go through various advanced concepts and patterns within k8s to take your game to the next level:</p><ul><li><a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/">Pod Probes</a></li><li><a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/">Pod Lifecycle</a></li><li><a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">Horizontal Pod Autoscaling</a></li><li><a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/">Pod Security</a></li><li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/">Pod Affinity and Anti-affinity</a></li><li><a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar">Sidecar container pattern</a></li><li><a href="https://kubernetes.io/docs/tutorials/stateful-application/">Running stateful applications</a></li><li><a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/">Operator pattern</a></li><li><a href="https://thenewstack.io/kubernetes-crds-what-they-are-and-why-they-are-useful/">Custom Resource Definitions (CRDs)</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=39011d8e35b2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implment backdrop in flutter with less than 10 LOC]]></title>
            <link>https://medium.com/@daadu/bakdrop-widget-for-flutter-4d4fdc98e757?source=rss-49de32e2100------2</link>
            <guid isPermaLink="false">https://medium.com/p/4d4fdc98e757</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[material-design]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <dc:creator><![CDATA[Harsh Bhikadia]]></dc:creator>
            <pubDate>Wed, 25 Jul 2018 16:06:26 GMT</pubDate>
            <atom:updated>2018-07-25T16:17:17.429Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rNUlJ-ihQbAcxiUt2K6EAQ.png" /><figcaption>1. Backlayer 2. Frontlayer 3. Subheader</figcaption></figure><p>Lets not waste time and get started with the code.</p><p>add dependecy:</p><pre>dependencies:<br>   <strong>backdrop: &quot;&gt;=0.0.0 &lt;0.1.0&quot;</strong></pre><p>run get package:</p><pre>$ flutter packages get</pre><p>drop in BackdropScaffold widget in your code:</p><pre><strong>import </strong>&#39;package:backdrop/backdrop.dart&#39;;<br><strong>import </strong>&#39;package:flutter/material.dart&#39;;<br><br><strong>void </strong>main() =&gt; runApp(<strong>new </strong>MyApp());<br><br><strong>class </strong>MyApp <strong>extends </strong>StatelessWidget {<br>  @override<br>  Widget build(BuildContext context) {<br>    <strong>return new </strong>MaterialApp(<br>      title: &#39;Backdrop Demo&#39;,<br>      home: <strong><em>BackdropScaffold(<br>        title: Text(&quot;Backdrop Example&quot;),<br>        backpanel: Center(<br>          child: Text(&quot;Backpanel&quot;),<br>        ),<br>        body: Center(<br>          child: Text(&quot;Body&quot;),<br>        ),</em></strong><br>      ),<br>    );<br>  }<br>}</pre><p>this is how it should look:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*qQruxCN5WxZdbctVQ9xKeA.gif" /></figure><p>I have writter a <a href="https://medium.com/@daadu/backdrop-with-flutter-acfa9fee7d2f">more detailed explaination</a> about <a href="https://pub.dartlang.org/packages/backdrop"><strong><em>backdrop</em></strong></a>package and <strong>BackdropScaffold</strong> widget in another article. Have a look if want to know more options.</p><p><a href="https://medium.com/@daadu/backdrop-with-flutter-acfa9fee7d2f">Quickly implement backdrop with flutter</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4d4fdc98e757" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>