Zen and the Art of Mind Tricks

All of us wish to be in a state of zen. We desire to be cheerful and have a positive frame of mind. We aspire to be clean of bad habits. We want to calm our monkey mind and experience a higher conscious.

Let alone achieving these, we find it tough to start.

faye-cornish-Uq3gTiPlqRo-unsplash.jpg

Below are mind tricks which help.

Timeout

Whenever you have an urge to do something you have resolved not to, do not give in instantly. Have a timeout after which you decide.

You have decided to go on a diet. An enticing scoop of ice cream is in front of you. Instead of immediately succumbing to your impulse, count till sixty. After that, decide whether you still want to gulp that scoop. In most cases, the urge would have died by then.

Reframe

Reframe unpleasant events in an empathetic manner; this nudges us from a feeling of angst to compassion.

We all go through bad experiences in life. Someone cuts us in traffic; a sales representative is rude to us. Whenever you have frustration creeping in due to things outside your control, reframe the situation.

The person who cut us in traffic is in a personal emergency and is trying to get somewhere. The salesperson, who was rude to us, is going through a terrible phase in her life.

Reframing a situation put us in a humane mode which drives away antagonistic thoughts.

Detach

Detach yourself from a frame of mind you do not wish to be in and observe your thoughts as a third party.

You want to meditate. You are not able to calm your monkey brain. Do not self-berate. Observe your fleeting thoughts as a third person. Do not curse your inability to control your mind. Watch the rise and ebb of ideas — the simple act of detaching and observing helps to calm the mind.

None of these tricks are my creation; I have summarized them in my own words. I have seen these repeated in various forms by experts in the field of psychology, wellness, and spiritualism.

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

Photo by Faye Cornish on Unsplash

Intuitive Introduction to Big O

The post is going to be an intuitive introduction to Big O notation. I am not going to be rigorous or mathematical in my approach.

Big O indicates how an algorithm behaves when the size of the input changes. The behavior might be either the computation time or the storage required for computation. What Big O tells is how the storage or computation time scales with the change in input.

The above is an intuitive definition of Big O.

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

A pivotal point to keep in mind is that Big O talks of change; it does not talk about specifics, i.e., Big O does not tell this algorithm will execute in 1 ms or 1 Mb of space is required for execution.

Let us understand Big O with a non-programmatic example.

There are lots of tennis balls piled up in a court. You have to devise an algorithm to move them to the clubhouse.

One approach is to pick a ball. Take it to the clubhouse and drop it there. Do the same with another ball. Repeat until there are no more balls left.

If there are n balls in the court, you have to go back and forth between the court and the clubhouse n times, O(n) is the Big O of this approach. As the number of balls increases, the work required to clean the court increases. If there are too many balls, with this algorithm, you might end up doing the task the whole day.

If you boil it down, what O(n) tells us is that the task increases linearly with the input, in this case, the number of balls in the court.

Another option is to collect all the balls in a bag in one go1. Take the bag to the clubhouse; this is a constant time algorithm. Since one is not repeatedly going back and forth for each ball; the task does not scale with the number of balls.

When trying to deduce the Big O of an algorithm, figure out the effect of input on storage space or computation.

fractal-880676_640

Why do we need Big O?

Big O has no significance when the input is small. For small input sizes, the efficiency of an algorithm does not make a difference — the effectiveness of an algorithm matters when the input size is large.

Coming back to our example, let us assume we are dim-witted and have institutionalized the back and forth algorithm. We have not bothered to figure out the Big O of the approach. Days pass by; there are at most a couple of balls in the court, and cleaning goes on nicely. One beautiful morning, there are ten thousand balls in the court, and we are in for an unpleasant surprise – we spend hours cleaning the court.

If we had bothered to figure out the Big O of our cleaning algorithm, we could have avoided this surprise.

When someone talks of Big O, they are usually talking about the worst-case complexity.

If the majority of the members of our tennis club are well behaved and do not leave behind balls in the court, then we would not have a tough time cleaning the court. But while calculating the Big O of the algorithm, we assume the worst case, i.e., no one cleans the court after their game, everyone leaves all the balls behind.

While calculating Big O, we do not take into account any of the constant time work done as part of the algorithm. In the first approach of cleaning the balls, if the person takes a break when she is midway through, Big O of the algorithm does not change; it is still O(n).

Why?

Reinforcing the critical point, Big O is the relationship between the algorithm and the input; Big O only cares how the efficiency of the algorithm varies with the change in input. The break the person takes does not vary with the input; if there are two balls in the court or a thousand, the person takes a break when she is halfway through. The break time or the number of breaks is not dependent on the number of balls in the court.

While calculating Big O, ignore any constant time work, i.e., work that does not scale with the input.

In recent times, technical interviews have put the spotlight on Big O. Big O is one of the notations to represent the efficiency of an algorithm. There are many others too.

1 The astute reader might observe that the time required to bag the balls increases with the number of balls in the court. We ignore this and assume that it is constant.

Image by Pete Linforth from Pixabay

Thoughts on Product and Feature Development

The post is a listicle on product and feature development in no particular order.

There are three rules for creating a successful product. Unfortunately, no one knows what they are.1

If the success of your product depends on changing a deeply ingrained habit, it is going to be challenging. Your product should be attractive for people to change their behavior.

For example, Uber changed a deeply ingrained habit of how one hails a cab. Initially, it was cool; hence, people did it. Now it is convenient. Before launching such a product, list down the motivations for someone to use your product.

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

Sometimes, the most crowded markets are ripe for disruption. Example – Dropbox. Dropbox was a late entrant in the file-sharing and storage market, but it worked flawlessly and conquered the market.

Competition may not always be harmful, especially when you are trying to create a new category. Category awareness is crucial, and if a big guy does it for you, you can piggyback on it.

If a startup had launched a smart speaker like Alexa or Google Mini, they would have two challenges. First would be to educate users on what a smart speaker is. The second would be to influence people to buy their product. Competition brings awareness to a category so that you can concentrate on selling the product instead of educating consumers.

kelly-sikkema--1_RZL8BGBM-unsplash.jpg

Think about product ownership and usage asymmetry. An online payment solution for schools is the perfect example. Parents want this. The want is not strong enough that they use this as criteria for picking schools. Any product that exhibits asymmetry like this needs to have powerful incentives for both sides.

Every time a customer reaches out to you; it is an opportunity for you to make your product better. Product enhancements should stem for customer service requests.

Users will find unique ways to use your product, which you would not have thought. Go with the flow.

More features are not always better. Be ruthless in culling features. New feature addition is a tug of war between simplicity and complexity. Irrespective of how small a feature is, it makes your product more complex which compounds over the long run. Even though adding a new feature is appealing, think twice before doing this. On the other hand, culling features is counter-intuitive. Be on the lookout for nixing features and simplifying the product.

Do not get attached to a feature based on the amount of effort you put, the technology used, or the uniqueness of the idea. Usage is the only benchmark for a feature’s success.

Customers do not always know what they want. Be careful while actioning on user feedback. Look around your house to see the plethora of unused stuff you brought thinking you need them.

In a consumer study, testers alternatively played French and German music in a supermarket selling French and German wines. Frech music resulted in more French wine sales while German music did the reverse. When quizzed, buyers were clueless about the music influencing their purchase.

Mix your product insight and intuition with customer feedback before acting on them.

You need something, does not mean the entire world is craving for it. There is no sure shot way to assess this but be aware of this.

Another corollary of the above.

You spot a problem does not mean others are looking for a solution to the problem. People are happy to live with minor inconvenience than change their habits.

Do not look at product features from your point of view. You might have a refined sense of UI, but your customers may not. Always assume a customer-centric viewpoint.

Assume no one reads anything. Figure out ways to make instructions implicit in product flows. My car displays a message on the dashboard when the service is due. It does not rely on me keeping a tab on this. If at all, you have to provide instructions, figure out what will make a user read it. The manual that comes with the Dyson vacuum cleaner has infographics familiarising the user with the product.

Treat customers differently based on their lineage. Someone new to the product needs more hand-holding than one who is used to the product.

New features do not pick up on their own. Figure out ways to incentivize a user to try out a new feature.

Do not be drawn to complexity. A simple feature trumps an overly complex one.

Figure out all the metrics to track before launching a feature. If you do this post-launch, it becomes a shifting goalpost where you are trying to prove the success of the feature rather than figure out whether it met the intended goal or not.

1 Quote Source

Photo by Kelly Sikkema on Unsplash

Why Work at a Startup?

I have been pondering for a long time – Why is startup experience invaluable? How is it different from working at a big corporation? I have a good vantage point on this as I have been part of many startups, traditional process-driven enterprise corporations, and in-betweens. I knew the benefit of the startup experience. I was finding it tough to put it into words. It clicked when I listened to conversations with David Epstein, author of the book Range.

David Epstein makes a case for generalists. He also talks about Kind and Wicked learning environments.

samuel-scrimshaw-NVlen5UZ7u0-unsplash.jpg

A kind learning environment is one where all the information needed to make a decision is available. In a kind learning environment, you get timely and accurate feedback too.

A game of chess is a kind learning environment. You have at your disposal the complete information needed to make a move. Once done, you get to know whether the move was right or not.

A wicked learning environment lacks all the information needed to make a decision. The feedback that you get is hazy and inaccurate. Luck plays a role in the outcome.

A game of poker is a wicked learning environment. You do not know the card your opponent has up her sleeves. Even if you play your cards right, you can end up a loser if lady luck frowns on you.

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

Working at a startup is more like a game of poker than chess. You are operating in a dynamic resource-constrained environment. You have too many things to do and not enough people to execute. You have to play the role of jack of all trades and master of none or one. You need to step out of your comfort zone. The business strategy is still evolving; you tweak it as you go – nothing is crystal clear or black and white. There is no one to guide you step by step. You need to make decisions with half baked information. Someone said – all startups are train wrecks inside.

Operating in such an environment is a fantastic learning experience. It is like packing a lifetime’s worth of education into a couple of years. It also forces you to look deep into yourself and check your biases and decision-making process.

Herminia Ibarra, an organizational behavioral specialist, says – “First act, and then think.” The reason being – “We learn who we are in practice, not in theory.” Startups give you a platform to do this.

Startups need generalists – people who can move up and down the technology stack as well as carry out many functions as and when required. In a startup, you get to work on the entirety of a product rather than a tiny weeny bit. You get a ringside view into what it takes to build an organization and run the day to day operations.

Being a generalist forces you to adopt the spiral method of learning. You learn enough to get your job done. You go back to it as and when needed and broaden your expertise. As a generalist, you get comfortable with not knowing everything and taking calls with the available information. You become comfortable with being uncomfortable.

Being able to don many roles lets you develop a broad mental model and good judgment – it gives you intellectual range. Also, it engenders curiosity – you want to learn more and more about a variety of subjects. When you are aware of many fields, you can borrow ideas from one and apply it to another. Charlie Munger calls it a latticework of mental models.

Please do not consider this as a case against working at a big corporation. The title of the post is – “Why work at a startup?” not “Why not work at a big corporation?”. All experience is valuable. In the future, I will write a counter post on why working at a big corporation is valuable.

If you like this, you should also read my old post on “What a startup is not?

Photo by Samuel Scrimshaw on Unsplash.

Security By Obscurity

Security by obscurity is one of the most common ills that plague the software industry.

The most cited analogy for this practice is the act of burying your gold under a tree versus locking it in a safe. Hiding your gold under a tree is security by obscurity. You are relying on the secrecy of the act for the safety of your asset. If someone uncovers this act, you are busted. Also, this gives you a false sense of security.

tom-roberts-Xbx4h70MIcU-unsplash.jpg

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

Recently my mesh router went kaput. I had to put in place a temporary alternative as internet connectivity is more important than oxygen these days. I had an old router given by an internet service provider. I tried to get it working with my current internet provider, but I could not. The router was locked to the provider. It would not work with any other internet providers. I tried to install OpenWRT on the router, but the hardware specs of the router were too low to host OpenWRT.

The other option was to change the custom firmware of the router to the router manufacturer’s default firmware. The manufacturer’s website had the steps to upgrade the firmware. I tried to follow the same. Strangely, the firmware upgrade section was missing in the router’s configuration interface. A bit of searching on the internet revealed that the internet company had removed this functionality from the interface, but if you directly go to the endpoint of the upgrade, it does work. They had just hidden this functionality in the interface. I did the update, and voila, unlocked the router.

This is textbook security by obscurity in practice. The internet company relied on only removing the upgrade link from the interface instead of eliminating the entire functionality. Anyone who discovers this is free to unlock the router. The right way to do it would have been to disable the feature as a whole. Not just obscure it in the interface.

Whenever you want to secure something, do not only rely only on obscurity. You can add obscurity as an additional security measure. For example, it is a common practice to change the ssh port on servers from the default port. Server admins do this in addition to a lot of other security lockdowns.

Photo by Tom Roberts on Unsplash

Distributed Tracing

Distributed tracing means different things to different people – depends on whom you ask.

In this post, we will look at distributed tracing in the context of debugging web application errors using logs in a microservices environment. We will also figure out the way to implement distributed tracing in the least disruptive manner. The aim is to leverage components that are already part of your application stack without introducing anything new for the sake of distributed tracing.

There are many closed as well as open-source products to get distributed tracing off the ground. There is nothing wrong in using these; in a lot many cases, the investment may not be worth it.

The idea of distributed tracing is straightforward. Every request that lands on your application should have a trace id – a random unique alphanumeric string. The trace id is usually called a request id. From there onwards, the request id should be part of whichever code path the request invokes within the application as well as any external calls it makes. The request id should also be part of the logs generated in all these paths. If the code path invokes external services, those calls should have the request id in the header. The application serving the external calls should follow the same pattern as discussed here.

There you go, the simplest possible distributed tracing implementation.

distributed-tracing-copy.jpg

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

One decision to make is where to generate the request id?

If you are serving a user-initiated action, generate the request id at the entry point of the request. Post that, all the subsequent code paths and dependent services will use the same request id.

The best place to generate the request id is the web server fronting the application server. All web servers have a way to add a custom header to an incoming request.

Below is how you would generate a request id in Nginx:


location / {
  proxy_pass http://upstream;
  proxy_set_header X-Request-Id $request_id;
}

Nginx doc – http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_id

We should generate a request id only if the request does not already have one. This sort of conditional generation can be achieved using the below configuration:


map $http_x_request_id $reqid {
  default $http_x_request_id;
  "" $request_id;
}

location @proxy_to_app {
  proxy_set_header X-Request-ID $reqid;
  proxy_pass http://backend;
}

Helpful links:

https://stackoverflow.com/questions/17748735/setting-a-trace-id-in-nginx-load-balancer

https://stackoverflow.com/questions/13583501/nginx-how-to-add-header-if-it-is-not-set/44761645

Also, make the request id part of web server access log.

Now that we have guaranteed all incoming requests have an id, we need to log the request id along with the application logs.

Most logging/application frameworks have a way to pass a thread-local custom context between layers. You can inject the request id in this context.

If you are on Java using Logback, MDC can help you to achieve this – https://logback.qos.ch/manual/mdc.html
If you are on Python and Django – https://github.com/dabapps/django-log-request-id
If you are on Go and Gin – https://github.com/atarantini/ginrequestid

Now that the access log, application log as well as any external calls made has request id, you can trace a request’s entire journey throughout the application and debug errors and pinpoint the cause.

Even if you are not trying to tie up micro service calls together, even within an application, having a request id helps in debugging issues faster – you can trace a request as it traverses through the different layers of an application.

Now that the strategy and tactic are clear, some enhancements; this is the right time to talk about span ids.

Let us say you have service A calling service B. Service A generates and sends a trace id as part of the call to service B. In addition to logging A’s trace id, service B should create a trace id of its own and log that too; this is the concept of a span id – an id owned by each service.

You can expand the span concept to a unit of work within a single service. For example, if your application has three different logical units, you can generate a span id for each of them too; depends on your use case and what is it that you are trying to achieve.

Having distributed tracing in place is a huge productivity boost. It is a low investment high ROI activity.

Designing A Great Meeting Room Experience

This post is a thought experiment in designing a great meeting room experience. This post will not go into the productive ways to conduct meetings but will deal with the mundane yet essential logistics part of meetings.

We will try to design the experience based on a couple of simple, timeless principles:
1. Nudging people towards proper behavior – Libertarian Paternalism.
2. Not relying on one’s will power to do the right thing – Ulysses Contract.
3. Designing the environment to influence productive behavior.

drew-beamer-9uX5cX1l3bw-unsplash.jpg

Get articles on coding, software and product development, managing software teams, scaling organisations and enhancing productivity by subscribing to my blog

Here we go; this is more of a listicle than structured prose.

If you want someone to do something, make it easy for them to do it – following this principle, it should be effortless to book a meeting room. The booking interface should list all the meeting rooms and the available free slots. It should also inform all the facilities a meeting room has like video conferencing, the capacity of the room, etc.

Outside every meeting room, there should be a display of the schedule for the day.

One of the often irritations is you landing up for a meeting in a room that you booked well in advance to find a paper sticking on the door saying the room is blocked for some critical visitor; this sort of overriding should be restricted to only a very few rooms.

How many times has it happened to you that you get up during a meeting to write something on the whiteboard to find the marker and the duster missing? A meeting room should be well-stocked with stationaries so that people do not have to step out in the middle of a meeting to fetch them.

There should be a large wall clock in every meeting room so that everyone is conscious of time. Even better would be a countdown timer which gives an auditory signal when the end of the meeting is near.

There should be a designated place in all meeting rooms to dump phones and laptops. One of the biggest distractions during a meeting is the constant barrage of notifications on devices and the pavlovian reaction to them. Do not rely on people controlling their will power to overcome this but design the right environment for people to achieve this.

Even though meetings have come to be associated with unproductivity, I believe collaboration and brainstorming are essential for crafting the right product. It is paramount that you do everything possible to facilitate communication and discussion between teams; this might make or break your product.

Photo by Drew Beamer on Unsplash