The Cruel yet Inspirational Sport of Boxing

Over the past two years, what’s surprised me about myself is that I’ve taken up an interest in the sport of boxing. The sport where two people put on gloves and get onto a 6 m square platform, enclosed by rope. As the bell sounds, over the course of 36 minutes, they start boxing each other with the ultimate goal of scoring a knockout. The smallest margin of error can change their lives, all the while millions of spectators are cheering them on.

Canelo vs. Golovkin
Canelo Álvarez and Gennady Golovkin, two of the best boxers in the world, when they clashed on September 16, 2017

While it doesn’t take long to realize the sheer brutality of boxing, I realized more things as I gained a better understanding of the sport. Here are three things I realized, while drawing certain parallels to society as a whole.

Cruelty Beyond the Actual Fights

Something which is plaguing boxing, and has been for decades, is systemic corruption.

If a fight goes the distance, the scorecards of three judges determine the victor. But judges score boxing, like figure skating and diving, in a subjective manner. Outside of obvious knockdowns, judges look for the boxer who best controls the action and acts as the aggressor. They also look for the boxer who lands the most clean and hard punches, and the boxer who is able to defend better. In other words, the scoring is in stark contrast to the black and white nature of tennis and golf.

Boxing also has a lack of regulation and oversight. There’s no central authority in the form of a national commission. The sport also lacks structure, as there are no tournaments, leagues or schedules (outside of amateur boxing).

In the midst of all chaos, the power brokers are the promoters. They set up the deals and arrange the fights. They can also be the ones responsible for the travel, lodging and food costs of the judges and the referee. Promoters can also have direct ties to the manager of a boxer, the very person whom should be representing the best interests of the boxer. In short, boxing has conflict of interest written all over it.

Roy Jones Jr. vs. Park Si-hun
Park Si-hun lifts the rightful winner Roy Jones Jr. into the air. Park retired from boxing after the Olympics, and Jones would go on to become one of the best boxers of his generation.

An infamous example of corruption in boxing, although this was amateur boxing, happened at the 1988 Summer Olympics in Seoul. In the finals, Roy Jones Jr. beat his opponent Park Si-hun in a one-sided affair. Yet, when the result was announced, the hands raised by the referee were Park Si-hun’s. Park had an embarrassed look on his face, and in a display of human decency, lifted Jones into the air.

In the recent superfight between Canelo Álvarez and Gennady Golovkin, one of the scorecards sparked controversy yet again. Amidst all the discussions following the fight, one that stood out to me was Teddy Atlas debating for an entire hour on ESPN. Never in my life have I seen someone speak with this much passion, while expressing their anger and disgust. And if you take the time to understand his background you’ll understand why. He’s a veteran trainer who loves the sport and has devoted his life towards it. He’s one of the few people who knows what boxers have to go through to be successful. He knows the sacrifices they have to make. He knows what they put on the line day in and day out. Yet, due to corruption, their hard earned accomplishments can be taken away from them in one fell swoop.

It makes you wonder. What hurts more? Taking all those punches leading up to that moment, or swallowing an unjust loss?

A personal takeaway from this, is that life can sometimes be brutally unfair. And it doesn’t even have to be as a consequence of corruption. We have to remind ourselves this every time we’re too fixated on a certain goal or ambition in life. We have to ask ourselves if we’re also enjoying the actual journey itself, rather than the thought of reaching the destination. Because one day, unforeseen things can happen beyond our control, preventing us from ever reaching that destination.

The Importance of Marketing

To be regarded as a great boxer, you have to prove you can beat other boxers that are perceived to be great. As boxing lacks structure, you cannot force an opponent to step inside the ring with you.

If you aren’t marketing yourself well as you rise through the ranks, a consequence will be that other good fighters will evade you. If your boxing skills are through the roof, but you cannot sell out arenas and generate pay-per-view revenue, it makes little sense for other promoters to risk their boxers on you. On the other hand, if you’ve built up your personal brand well, opponents will line up to fight you even though they have little chance of beating you.

Some boxers have a harder time than others. They lack natural charisma. Their fighting style is too technical, as opposed to being an aggressive knockout artist. They don’t come from a country where the entire nation will rally behind them.

Floyd Mayweather and Conor McGregor face-off
Two master marketers in Floyd Mayweather and Conor McGregor showing how it's done

The boxer who mastered the art of marketing was Floyd Mayweather, having generated $1.3 billion in revenue throughout his career. Through boastfulness and flaunting his wealth, he created a persona that people hated. As he was such an exceptional boxer, he dangled his undefeated record like a carrot on a stick. Casual fans were paying for the chance of seeing him finally lose, while hardcore fans marveled at his skills.

Earlier this year, Mayweather came out of retirement to fight Conor McGregor in a boxing match. As they both walked away with hundreds of millions of dollars after the fight, I can’t help but think about a subject I touched on before. While both men are entertainers and great in their own right, the fight sold as well as it did because people believed McGregor had a chance. The marketing campaign led people to believe this would be a competitive match, rather than a spectacle. It was successful, because the average person doesn’t realize that, despite boxing and MMA being combat sports, they are still worlds apart. Leading up to the fight, when high-profile boxers (without a vested interest in marketing it) were asked about who would win the fight, you could tell it annoyed them. They felt that the suggestion alone of McGregor having a chance was disrespectful towards the sport of boxing.

This is a feeling I can relate to every now and then when it comes to software. I feel like some people, who lack an understanding of software and what it takes to create great products, marginalize the very profession I care so much about.

The Inspirational Side of Boxing

Looking beyond the cruel surface of boxing, what I find is something inspirational. It astounds me that there are people out there with the competitive spirit to step into the ring and excel. That there are people out there born into poverty with all the odds stacked against them. But because they had that innate drive, they endured more hardships and ended up forging a better future for themselves and their family.

The fight is won or lost far away from witnesses - behind the lines, in the gym, and out there on the road, long before I dance under those lights.
- Muhammad Ali

In the world of boxing, my favorite quote is by the late Muhammad Ali. Today, due to the Internet and social media, we focus a lot on instant gratification. We read about accomplishments and watch highlights and award shows. We see couples in happy relationships. We see athletes break world records. We see actors put on masterful performances. We see entrepreneurs sell their startups for millions of dollars. What we don’t see, unless we look for it, are the tens of thousands of hours of work they’ve put in to get to where they are.

Recounting a Year of Overhauling An E-commerce Solution

New Relic chart
In 2017, our Magento application's response time is below 140 ms. Before the end of 2015, it was still hovering around 1000 ms.

So far, I consider what I did during my first year at Paradox Interactive to be my greatest accomplishment. During that timespan, I reduced our Magento application's response time from 1000 ms to 140 ms. I also increased its reliability, paid back some technical debt and took ownership of the entire stack. During the beginning of 2016, I deployed the biggest improvements. For that whole year, compared to the year before, the conversion rate of our Magento store increased by 59%. Revenue also doubled.

As I've departed from doing Magento development since then, I thought I'd closing out this chapter of my career by recounting two memorable challenges during that eventful year.

Integration Woes

Our e-commerce solution uses Adyen to handle payments. While we only sell digitals products today, we also sold physical products in the form of merchandise back then. Our own API backend delivers the digital orders, while a solution called Shipwire fulfills the physical orders.

Adyen Critical Bugs

Adyen logo

The way we integrated with Adyen was through their Magento plugin, which wraps Adyen's API. The primary goal of Adyen and that plugin is to set orders to complete upon successful payment. However, every now and then we would come across orders that got stuck and never progressed to complete. The reason this was happening was due to a race condition, as a result of how that plugin handled callbacks from Adyen. If a callback says a payment was successful, that plugin would update the corresponding order object. As a callback is an HTTP request, spawning a new Apache process, there exists a window of opportunity where the new process has handled the callback while the original process is still updating the order object.

Adyen released a new version of their Magento plugin, fixing amongst others this particular issue. As this version of the plugin seemed to contain large amounts of refactored code, I thoroughly tested it and discovered a critical bug: orders that only contained digital products would never progress to the complete state. While not evident at first, this was because the plugin didn't take into account that order objects can have an absent shipping address in Magento.

Another problem, relevant to us, was how the plugin addressed the race condition. Instead of processing callbacks immediately, the plugin stores callbacks in the database. A cron job is then run every minute to process callback events older than 5 minutes, which added a delay to what we deliver to our customers. As I couldn't see a better, quick solution, I patched the logic to 1 minute.

At a later time, we needed to upgrade our plugin again. While everything seemed fine, something odd was occurring as orders poured in when we released an expansion for one of our games. For some reason, the amount of orders stuck started piling up. Only after two hours of debugging did I understand what had happened:

if($order->getIsVirtual()) {
    $this->_debugData[$this->_count]['_setPaymentAuthorized virtual'] = 'Product is a virtual product';
    $virtual_status = $this->_getConfigData('payment_authorized_virtual');
    if($virtual_status != "") {
        $status = $virtual_status;
        
        // set the state to complete
        $order->setState(Mage_Sales_Model_Order::STATE_COMPLETE);
        
    }
  }
Magento will throw an exception if you try to set an order's state to complete in Magento

When processing callbacks, and for orders containing only digital products, the plugin executes a line of code that sets the state of the corresponding order to complete. In Magento, the order object is a state machine. Directly changing the state, and to complete in particular, will throw an exception. This block of code also seemed unnecessary. The order object is already complete before it's executed.

The reason orders piled up was because the cron job could only process one successful order per minute. As the cron job runs it loops through each callback and corresponding order, but crashes after the first iteration. I didn't spot this bug while testing because I never made enough orders in quick succession to notice something was wrong. It was also hard to immediately understand what was going wrong, as exceptions from cron jobs triggered by Magento don't end up where they usually go, but to a table called cron_schedule in the database.

While I find Adyen to be a superb payment provider, I learned something important. Coinciding with what I observed while working for a large e-commerce firm, e-commerce is still dominated by physical products. If you sell digital products you have to be extra careful with plugins. They are poorly tested for (evidently not in our case) and work under assumptions that may not be true for digital products. The 5 minute race condition also illustrates this. If you sell physical products, adding a 5 minute delay before an integration can pick the order up for shipment doesn't have as adverse of an effect on user experience as for digital products.

Shipwire Order Fetching Logic

Shipwire warehouse
Shipwire handles inventory and fulfills orders

The selling point of Shipwire is that they handle your physical inventory in their warehouses, and fulfill orders for you. While it, similar to Adyen, offers an API, we were using a Magento integration they had built. You fill in the credentials of an API user of your store, allowing Shipwire to every now and then poll unfulfilled orders.

On occassion, it would miss picking up orders. In contrast to Adyen's Magento plugin, the code Shipwire runs is invisible to us making it hard to debug. To complicate things, Shipwire doesn't communicate through a REST API but SOAP, and you can't manually trigger a polling attempt.

In the end, I added a snipped of logging code to a method that all Magento API calls pass through. After examining what endpoints Shipwire called and the payloads, I realized the flaw. As you'd expect, Shipwire fetches all paid for but not shipped physical orders. But the request also applies a filter, fetching only the orders that have an updated_at timestamp later than the last order Shipwire picked up. While this filter is sensible, it doesn't take into consideration the fact that newer orders can be ahead of older orders in their progression. Some forms of payment take longer than others, and customer service might update an order a day or two later.

As it was clear Shipwire's support doesn't handle technical issues of such detail, I solved this problem by overriding the method that all Magento API calls pass through. The overriding code intercepts all requests from Shipwire that try to fetch orders, and subtracts 30 days from the updated_at filter.

Another solution would have been to write our own integration, directly towards Shipwire's API. An opinion that I've formed is that, as your e-commerce solution matures, you should strongly consider moving away from platform-specific plugins and integrations. You should instead write your own integrations towards a solution's "core" API. The core API is by necessity much more tested and stable. While using platform-specific plugins let you get started quickly, they tend to carry two major drawbacks: they are bloated as they need to cover a wide range of use cases; and they are developed by those who are knowledgeable about either the core API or the platform, but not both.

API Backend 504 Gateway Timeouts

This was without a doubt one of the most elusive bugs that I've encountered.

Our store uses our own backend API for a number of things, with the most important ones being account integration, order fetching, and delivering Steam product keys. On rare occassion, calls to our backend failed which could result in a customer not getting their product keys. Through adding better logging to our Magento codebase, I found out that these failures occured for all endpoints. Each failure would result in a response containing an empty body, with a header of "HTTP/1.1 504 GATEWAY_TIMEOUT".

Besides the difficulty of reproducing this, each request passes through a vast amount of servers and services. Our backend is an extensive Amazon Web Services stack where requests go through NGINX, Elastic Load Balancer, Elastic Beanstalk, Apache and Tomcat before reaching our Scala codebase. The response from our backend then has to go through NGINX, Varnish and Apache on the Magento side. After ruling out Magento, my colleague who works with our backend did a series of investigations.

He tweaked timeout and KeepAlive settings, to no avail. He performed analyses on our logs and found that the number of 504 Gateway Timeouts correlated with the number of requests, but there was neither a relation to latency nor load.

In the end, my colleague discovered what had haunted us for almost a year. As I lack in-depth knowledge about our backend, here's how I understood it: our backend nodes have Apache in front of them. Apache was configured to logrotate every minute. Whenever that happens, Apache reloads, thereby dropping all existing requests.

Managing a Managed Host

A consultancy company used to manage our e-commerce solution. They deployed our store on a managed host, managed by a hosting provider. This meant that neither the consultancy company nor we had root access to the server.

feelsbadman

If you're someone who has some experience managing servers, this is a frustrating situation to be in. Part of that frustration was because it amounted to lots of communication time. We couldn't perform trivial tasks such as setting up Newrelic, adding a virtual host or changing a configuration file without going through the hosting provider. The user which we finally had them create for us had so few permissions at first, that we couldn't even read our application's log files.

Another part of that frustration was that they were lacking in whatever solution they were using, as well as in their sysadmins. They lacked transparency and weren't following best practices (to the limited extent of my knowledge). We had to hold their hands too often, and if a problem occurred they didn't attempt to understand the root cause and take measures to prevent it from happening again. To cut them some slack, the vast majority of those who use a managed host are non-technical. Their other clients are thus less likely to see their shortcomings, meaning they can get away with a poorer level of service.

For instance, I recall three incidents that highlighted the challenges:

Backups to the Same Disk

One time, I backed up our production database before a deploy, with the intention of removing it the next day. That night, I received a flood of alerts from Newrelic. To my horror, I realized Magento was returning 503 errors because our server was out of disk space! While our hosting provider answered my email and freed up space, I realized what had happened the following morning: their solution performs nightly backups, but saves the backups to the same disk! The same backups were also causing our application to hang every midnight.

For this particular incident, I was also at fault as I shouldn't have backed the entire database. I should've just backed up specific tables of interest. That way, the nightly backup wouldn't have used up as much additional space.

Varnish 503 Service Unavailable

Our hosting provider waas using an unnecessary server setup. Our server was set up as HAProxy > Varnish > Apache. Varnish was not configured to do anything, and we didn't need load balancing as we were on a single, powerful and underutilized server.

During four occasions throughout the course of two months, all customers ended up getting 503 errors from Varnish when they tried to login or make a purchase. This was odd, as it was something which had never happened earlier. It was also hard for me to debug, as I had access to neither Varnish nor HAProxy. The little access I had to Apache was restricted to our DocumentRoot directories. To make matters more frustrating, every time I bugged our hosting provider to troubleshoot, the problem somehow disappeared. They would then drop the investigation, leading to the same problem resurfacing a week or two later.

In a desperation attempt the fourth time it happened, I asked our hosting provider if they had checked /var/log/ of our server. It was then that they found "zend_mm_heap corrupted" at the end of the Apache error log, the key clue which solved the mystery: our hosting provider periodically upgrades packages on their managed hosts. This time, we ended up with a version of PHP and an OpCache which could cause segmentation faults. These faults had a tendency of only triggering after Apache has run non-stop for several days. Hence, whenever we contacted our hosting provider to troubleshoot they would inadvertently fix the problem by making random tweaks to configuration and restarting.

What surprised me the most about this, was how they didn't even look in one of the first few places you'd look. Also, throughout the whole process they didn't even inform or play with the thought that the package upgrades could've been behind this critical bug. Going back to the server setup, they would've also been less confused if HAProxy and Varnish were not used at all.

HAProxy Misconfiguration

As a final example, there was also an incident when we asked them to swap our wildcard certificate for an EV certificate. When carrying out the changes, they messed up X-Forwarded-Proto in HAProxy so it had a value of "https https". This was allegedly due to a bug in the control panel they were using. This caused our store to become unavailable as users ended up in a redirection loop. While mistakes do happen, this particular mistake took them 30 minutes to rectify. They simply didn't back up the configuration file, so had trouble even spotting the problem.

The Successful Migration

During the second part of that first year, I had gained a well enough understanding of our e-commerce solution and pulled the trigger on migrating it. The goal was to be able to gain better control, and not let a hosting provider cause us distraction. Also, this gave us the opportunity of using PHP 7 which had just become available.

The migration project involved several phases: picking a hosting provider, setting up servers, testing our solution on PHP 7, writing bash scripts for the migration, and performing test migrations.

A couple of days before the migration, we lowered the TTL of our domain's A records. I deployed our codebase and moved over all the media assets. On migration day, I put both our old store and new store in maintenance mode while our IT manager updated the A records. A bash script was then run to migrate the MySQL database as well as the Redis database. Once completed, I took our new store off maintenance mode. The downtime ended up not being more than 15 minutes. (Had I performed the migration today, I would've taken advantage of replication.)

A challenge with the migration involved the amount of communication and coordination required. I decided the exact date and time of the migration together with Marketing and Sales. This was then communicated to other parts of our organization, as well as to both our old and new hosting provider. I also tasked our old hosting provider with forwarding all traffic to the new server.

Honorable Mentions

Screenshot of in-game store
The minimalistic store with its base theme and made up product catalog

Besides the integration and hosting provider woes, there were a few other memorable challenges.

The original codebase of our store wasn't in the best shape, which was something I improved over time. A prominent problem was that almost all the code used for the integrations were crammed inside of a God class. While troubleshooting integration problems and implementing new features, I broke this class down into several where each had a single responsibility. I also reduced tight coupling and removed needless dependencies. For instance, one requirement is that if a customer changes their address during checkout, the new values need to be synced to our backend. Much of this requirement was implemented in the frontend by sprinkling some jQuery into the checkout templates. This causes an unnecessary distraction whenever you redesign your checkout.

I also built a store view for Magento, intended for selling expansions and DLC inside our games through an in-game browser. The hard part involved making the store fast and minimalistic. To do this, you have to have a good understanding of how Magento and particularly the checkout works. In addition, I also created acceptance tests in Selenium covering the entire purchase flow. In the end, this store never launched as it clashed with Valve more than we had anticipated. This was understandable, as Steam players purchasing through this store would deprive Valve of their 30% share.

Reflections

Looking back at the successful year, I feel an overwhelming amount of gratitude. Much of the success was made possible by what I learned at my previous job (a leading Magento consultancy company). My former colleagues inspired and challenged me to learn more about software development, and particularly about PHP, Magento and object-oriented programming. One colleague taught me something that will stick with me for a long time: you shouldn't just blindly learn how to do something. You need to go beyond that, and seek to understand how things work behind the layers of abstraction.

The successful year was also made possible by my manager and closest colleagues, who gave me a lot of freedom to improve our e-commerce solution. It also illustrates the importance of continuous product improvement. While we tend to get lost focusing on new features and the number of them, it's important not to lose sight of the core features of a product. For those core features, we need to endlessly ask ourselves if they can be improved and carry out these improvements.

Everything Is More Complex Than We Think It Is

To this day, I still remember a short conversation I had with a classmate. This was back in 2013, when I was still studying in college. During recess, we discussed potential companies we'd like to work for. As I answered Spotify, my classmate remarked "Huh? Why would they need any developers? I mean, they already have a finished product!"

That same year, Spotify had in excess of 900 employees worldwide of which 300 were engineers, while continuing to expand.

I remember this particular exchange, because it exemplifies how everything in life is drastically more complex or harder than we think it is. It's only once we take the time to understand or experience something, that we start grasping the extent of our ignorance. What's also interesting is that the less we know, the more prone we are to having larger disparities between our perceptions and reality, with seemingly no upper limits. And this can remain unchanged, no matter how often we interact with the very things we are ignorant about.

While this phenomenon has impacts on society in more ways than I could ever imagine, I'd like to discuss it in the context of software. Before getting there, I'd like to bring up sports as it's more relatable to most people while sharing similarities to software. To round things off, I'll reveal how my mindset has changed upon understanding this phenomenon.

Watching a Sport We Know Little About

Remember the last time you watched a sport that you knew little to nothing about? That sport might've been basketball. It might've been golf or ice hockey. It might've even been an esport such as Counter-Strike or League of Legends.

Lebron James and Kobe Bryant
Two superstars of basketball, Lebron James and Kobe Bryant

If you don't know anything about basketball, all you'd see during a match between two teams is a bunch of tall people running around with no rhyme or reason, trying to throw a ball towards a hoop. If you don't know anything about Counter-Strike, all you'd see are a bunch of overgrown children glued to their monitors, pushing keys with one hand on a titled keyboard while doing flick motions with the other. As a personal example, whenever I come across golf highlights, I can't register much beyond seeing people swinging a stick.

But, as you understand more of that sport through playing it or becoming an avid fan of it, you start noticing more and more nuances and intricacies. Using basketball as an example, you notice the subtle movements players make to freeze their defenders for a split second. You also start understanding how a player's ability is a function of a myriad of skills. These skills can be obvious ones like shooting, dribbling, passing and athleticism. But there are also less obvious skills like game awareness, experience, coachability and your ability to cooperate with and inspire teammates. To top things off, each skill also expands into more skills which in turn also do the same. And the more someone knows about basketball, the more they'll be able to identify the sheer number and depth of skills.

Although you may not know much about a particular sport, consider this fact: hundreds of millions of people play association football and basketball around the world. Out of all those people, some have managed to distinguish themselves as the top 100 players in a sport. While that in itself is an astonishing accomplishment, there are players among those 100 that have managed to distinguish themselves even further. This speaks volumes about the complexity of sports.

Software In Comparison

When it comes to software, I speculate that it's harder for a person to understand and appreciate the complexities of it, in comparison to other fields.

To explain myself, think about some of the most complex software in the world: Google Search, Facebook, Youtube, Spotify, Chrome, Android and Windows (seeing this list may already be a surprise). No matter how large or complex each of them are, they're invisible. This is in stark contrast to buildings, where any reasonable person would realize a skyscraper is more complex than a tent. In addition, these applications and operating systems are ridiculously accessible today, to the point of being literally at our fingertips. A sheer amount of work is also put into polishing their design and user experience. As an end user, you don't see the amount of talented personnel required to build these applications. You don't see the massive infrastructures in place. You don't, for the most part, notice the countless iterations taking place. You also certainly don't see the thought applied to the files, classes and functions that make up the millions of lines of code.

One World Trade Center
It's mind boggling to think that Google Search, with such a simplistic interface, would be the building equivalent of One World Trade Center when compared to other software

Also, in contrast to sports, software and developers don't remotely garner the same amount of public attention or respect for being great. Although we constantly see highlights of spectacular plays and accomplishments in sports, the same very rarely happens when it comes to software. Admittedly, while watching a high-flying dunk is way more exciting to me than watching someone code a great application, I do wonder how things will change in the future. As software becomes even more prevalent and more and more kids experience what it means to create and not just use software, I wonder if our society will start holding software in higher regard.

The Quality Gap Between Software and Skill Gap Between Developers

Of all the things sports and software have in common, it's worth highlighting one thing: There are vast differences when it comes to the quality and usability of software. For instance, I recently learned that Spotify returns the first second or two of each song together with the search suggestions. This way, you don't even notice that the songs you're playing actually don't originate from your hard drive. It's without a doubt one of countless things Spotify does to enhance user experience.

Contrast this with a bad application, where you struggle to even click on a search box. Once you do manage and start typing your query, search suggestions appear slowly because there's no local cache and no index set up on the backend. To add insult to injury, you have to type the full and exact name of songs or artists, including any apostrophes or dollar signs. To make matters somehow even worse, odd covers of songs appear before the original song in the result set. This is assuming that the backend is working, because it's constantly under heavy load from handling expensive queries that are neither debounced by the application or throttled by the backend itself.

When it comes to developers, there's also a gigantic skill gap between average and great. This is because, like basketball, a developer's skill is dependent on such a large variety of skills, where each skill branches into skills of greater and greater detail. The list is long and, like basketball, includes things that are less obvious. For example, I only recently realized that empathy belongs on that list. By having empathy, you're more considerate about end users as well as the next person maintaining your code. Having empathy also allows you to better understand the needs of your colleagues as well as the company as a whole. Empathy is also an important component of communication, where you need to adapt how you deliver your message depending on your audience.

While the existence of these gaps might be baffling, it's no more different than the gaps that exist in sports. From the outside, Lionel Messi is not much different from any other person. Yet, his ability as a football player is worlds apart better than the average person.

How My Mindset Has Changed

Although I didn't go on to graduate from college, it was instrumental in helping me begin to realize the complexities of the world. Compared to how I was five years ago, I approach things with a greater degree of humility and have become a more introspective person. I realize there's a lot to learn in this world, and that the moment I think otherwise, is when I've become too ignorant. I've also developed a tremendous amount of respect for and look up to people who are great at their profession, regardless of what industry they work in.

What I've also learned is that, as a developer, I prefer working for companies that hold the quality of its software in high regard. Both because quality has a clear and recognized business value to them, and because the companies in question understand that building such software requires great talent, resources and attention to detail. By the same token, I also understand that such companies set a high hiring bar which is one of several reasons why I continously strive to improve.