<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Denat Hoxha</title>
    <description>The latest articles on DEV Community by Denat Hoxha (@denat).</description>
    <link>https://dev.to/denat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F210412%2F112913a6-1330-4c5e-b89c-54c01b305998.jpg</url>
      <title>DEV Community: Denat Hoxha</title>
      <link>https://dev.to/denat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/denat"/>
    <language>en</language>
    <item>
      <title>Sharing Data Between Microservices</title>
      <dc:creator>Denat Hoxha</dc:creator>
      <pubDate>Tue, 25 Oct 2022 17:20:25 +0000</pubDate>
      <link>https://dev.to/denat/sharing-data-between-microservices-316l</link>
      <guid>https://dev.to/denat/sharing-data-between-microservices-316l</guid>
      <description>&lt;p&gt;When I started working with microservices, I took the common rule of "two services must not share a data source" a bit too literally.&lt;/p&gt;

&lt;p&gt;I saw it stapled everywhere on the internet: "thou shalt not share a DB between two services", and it definitely made sense. A service must own its data and retain the freedom to change its schema as it pleases, without changing its external-facing API.&lt;/p&gt;

&lt;p&gt;But there's an important subtlety here that I didn't understand until much later. To apply this rule properly, we have to distinguish between &lt;strong&gt;sharing a data source&lt;/strong&gt; and &lt;strong&gt;sharing data&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why sharing a data source is bad
&lt;/h2&gt;

&lt;p&gt;An example: the Products service should own the &lt;code&gt;products&lt;/code&gt; table, and all the records in it. They expose this data to other teams via an API, a &lt;code&gt;products&lt;/code&gt; GraphQL query, and the creation of these records via a &lt;code&gt;createProduct&lt;/code&gt; mutation.&lt;/p&gt;

&lt;p&gt;The Products service has ownership over the products source of truth, and no other team should reach into this directly, ever. If they want data out of it, they should ask the Products service for it via the contract (API) they adhere to. Under no circumstance should you allow direct access to the database, or you’ll lose the freedom to make changes to your schema. &lt;a href="https://www.denhox.com/posts/four-lessons-learned-building-microservices/#services-should-hide-their-internal-details"&gt;I learned this the hard way.&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing data is OK
&lt;/h2&gt;

&lt;p&gt;The fact is, services need data that belong to other services.&lt;/p&gt;

&lt;p&gt;Example: A Trip service will need access to passengers (from Passenger service) and drivers (from Driver service) to serve trip overviews.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--91DWvkup--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfbxz7r93gti4l3odf8f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--91DWvkup--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfbxz7r93gti4l3odf8f.jpg" alt="A simple MS architecture with three services" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trip service asks each respective service for its data, synchronously, to fulfill the original request (&lt;code&gt;getTrips&lt;/code&gt;). We can rest assured that the data is fresh, and the requesting client will get a &lt;em&gt;strongly consistent&lt;/em&gt; view of the data (some of you might see where I’m heading at this point ;).&lt;/p&gt;

&lt;p&gt;This synchronous request/response model to transmit data between microservices is a very natural mental model for teams that start off in microservices, at least in my experience. You need some data, you know where to get it, you ask the owning service for it, it gives the data to you, &lt;em&gt;on demand&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On top of that, serving fresh, &lt;em&gt;strongly consistent&lt;/em&gt; data was a no-brainer for teams I was in. Strongly consistent data means up-to-date data, the absolute “freshest” piece of data, straight from the source (of truth). To me, back then, serving anything other than consistent data was unacceptable. How could you serve anything other than up-to-date data? Anything else would be a lie!&lt;/p&gt;

&lt;p&gt;We applied these patterns as dogma because we saw no other way, and above all, it felt natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronicity and strong consistency don't scale
&lt;/h2&gt;

&lt;p&gt;Architectures that rely heavily on synchronous requests and strong consistency do not scale well. Sometimes it’s just not feasible, or strictly necessary, to always go straight to the source for your data needs.&lt;/p&gt;

&lt;p&gt;The Trips service example above looks neat at first, but it’s rarely the case that systems remain that simple. New services are born, and they’ll require data from existing services. Sticking to the synchronous request pattern will, in time, have you end up with a tangled web of requests between services. Here’s a scenario:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AMbWuAmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i96qt6qt5uqkcik9vcqy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AMbWuAmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i96qt6qt5uqkcik9vcqy.jpg" alt="Example flow with synchronous requests" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user completed a challenge, runs a &lt;code&gt;completeChallenge&lt;/code&gt; mutation to Challenge service&lt;/li&gt;
&lt;li&gt;After storing the completion, Challenge service lets the Leaderboard service know, so it can update the leaderboard&lt;/li&gt;
&lt;li&gt;The Leaderboard service asks User service for user display names and avatars to build the new leaderboard state&lt;/li&gt;
&lt;li&gt;Leaderboard service sees that there’s a new leader in the new leaderboard state, lets Notification service know so it can notify participants that there’s a new leader!&lt;/li&gt;
&lt;li&gt;Notification service asks User service for the up-to-date email addresses of the users in that particular leaderboard, so it can send emails out&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The User service is clearly a point of contention here: everyone is in one way or another depending on it. Imagine this service being down: it’ll render most of the other services down as well. Not only that, but you’re gonna have to make sure to keep this server buffed up at all times with more replicas and a high-performant DB to keep up with the demand.&lt;/p&gt;

&lt;p&gt;On top of that, each hop in this chain of requests adds latency to the entire request. Each hop has potential to add an exponential amount of latency, because every service in the dependency chain could fire off more than one request to its own dependencies. Before you know it, you’ve hit unbearable levels of latency.&lt;/p&gt;

&lt;p&gt;Finally, every additional dependency in the request chain increases the likelihood of the entire request chain failing. In a request chain involving five services with an SLA of 99.9% (~9h of yearly downtime), the composite SLA becomes 99.5%. That’s almost 2 days of downtime per year!&lt;/p&gt;

&lt;p&gt;We can avoid all of these downsides by asking one question: do services really need up-to-date data?&lt;/p&gt;

&lt;p&gt;The Notification service (step 5) arguably does. If a user changed their address and the Notification service doesn’t know about it, it’ll risk sending an email to the wrong address and not getting the notification to the intended user.&lt;/p&gt;

&lt;p&gt;The Leaderboard service, on the other hand, probably doesn’t need the up-to-date display names and avatars to build the leaderboard — it’s not that big of a deal if users see stale avatars or display names.&lt;/p&gt;

&lt;p&gt;As you can see, services have different data consistency needs. There are trade-offs that we can use as leverage to apply different data sharing methods and build a more robust distributed system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Eventual Consistency
&lt;/h2&gt;

&lt;p&gt;It’s at this point in my career that I discovered that services can maintain a copy of other services’ data, locally in its own DB tables. It comes with the responsibility to maintain that data, either via events or polling.&lt;/p&gt;

&lt;p&gt;Included in this package is the fact that the data may be stale for some time, but that it’ll eventually be updated, meaning the data is &lt;em&gt;eventually consistent&lt;/em&gt;. We can’t guarantee that the data isn’t stale, but we can guarantee that we’ll eventually catch up.&lt;/p&gt;

&lt;p&gt;The moment it “clicked” for me was when I thought about it from the perspective of a backend service depending on a public weather API for weather data. Instead of retrieving weather data for Prishtina or Berlin every time a user from those respective cities needs weather data, I cache it (maybe multiple times a day) by materializing it in a local table and serve cached data to those users. I’ve made the trade-off in favor of eventual consistency because it’s not crucial for my users to see &lt;em&gt;the most fresh data&lt;/em&gt;, it’s fine if it’s a few hours stale.&lt;/p&gt;

&lt;p&gt;Going back to the Challenge example: we can cut off many synchronous dependencies to the User service by just maintaining a local copy of users in the services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leaderboard service can maintain a local copy of users and avoid having to make requests to User service. No one really minds if the data is a bit old, it’s not a showstopper if someone sees a slightly old avatar.&lt;/li&gt;
&lt;li&gt;Challenge service can do the same; say if it exposed a &lt;code&gt;getChallengeDetails&lt;/code&gt; query and needed user display names and avatars to show the current challenge participants — it can also serve this eventually consistent data from its own materialized users table.&lt;/li&gt;
&lt;li&gt;Notification service, although a bit more sensitive, can also utilize data sharing to remove its dependency on Users service. It can materialize users locally and maintain a best-effort updated state by listening to User updated events to ensure it has the most up-to-date emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although we didn’t talk much about &lt;em&gt;how&lt;/em&gt; services share this data (a topic for another time that I’m currently writing up!), a final example architecture would make use of a combination of event sourcing and caching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dyQtmw0O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9k4m0v2haqsyoygugsp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dyQtmw0O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9k4m0v2haqsyoygugsp.jpg" alt="Example architecture with two main methods of sharing data between services: event sourcing and caching" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want more examples, take a look at &lt;a href="https://medium.com/fiverr-engineering/how-to-share-data-between-microservices-on-high-scale-ab2bc663898d"&gt;How to Share Data Between Microservices on High Scale&lt;/a&gt; by Shiran Metsuyanim, an engineer at Fiverr. It’s a great post that shows how to maintain robustness when adding a new service, by laying down the constraints, and then discussing the trade-offs between the possible synchronous, asynchronous, and hybrid solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I wanted to get this point across to developers that, like me a few years ago, are stuck in the literal sense of “do not share data” but must realize that this only applies to not sharing the source of truth. Maintaining a copy of a service’s data in another service’s domain is perfectly fine, and embraces eventual consistency, a handy tool with its own trade-offs.&lt;/p&gt;

&lt;p&gt;In a (very near) future post, I’ll be discussing the many possible solutions of sharing data between microservices.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Benefits of Fail-fast Systems</title>
      <dc:creator>Denat Hoxha</dc:creator>
      <pubDate>Mon, 30 May 2022 10:14:00 +0000</pubDate>
      <link>https://dev.to/denat/the-benefits-of-fail-fast-systems-24h5</link>
      <guid>https://dev.to/denat/the-benefits-of-fail-fast-systems-24h5</guid>
      <description>&lt;p&gt;How do you handle an unexpected failure or system state?&lt;/p&gt;

&lt;p&gt;Do you handle it gracefully and do a best-effort attempt at returning a response? (&lt;em&gt;fail-safe&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Or, do you stop the failure in its tracks, let it blow up, and sound the alarms? (&lt;em&gt;fail fast&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;The general approach that has worked best for me is to &lt;strong&gt;always fail fast unless you’re close to the end-user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The user interface (frontend) is the closest you can be to the end-user. When running into a failure, fatally crashing the UI is the absolute worst response. &lt;em&gt;Any&lt;/em&gt; type of response would be better than a fatal crash: serving cached (but stale) data, partial responses, or a retry button. As a last resort, you should be showing a general “Whoops, something broke!” message.&lt;/p&gt;

&lt;p&gt;It’s worth putting in the effort in determining the failure state combinations of our frontend to gracefully handle failures and ultimately deliver a good experience. A bad user experience causes churn and uninstalls.&lt;/p&gt;

&lt;p&gt;However, in any other situation, failing fast is a vastly superior strategy to make your systems more robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failing visibly makes bugs easier to find
&lt;/h2&gt;

&lt;p&gt;When your service encounters an invalid state, failing fast means &lt;strong&gt;failing visibly&lt;/strong&gt;. After halting execution, make it clearly visible that your system has encountered an invalid state.&lt;/p&gt;

&lt;p&gt;Failing visibly makes defects much harder to miss. Even if you have logging in place (which you should have anyway), errors are much more likely to be noticed if all involved parties have it “blow up in their face”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easier debugging
&lt;/h2&gt;

&lt;p&gt;Continuing execution after an invalid state makes your system much harder to debug. Instead of knowing exactly where execution stopped, you’ll now have to deal with stepping into your code, reproducing the scenario, and figuring out at what point your program diverged into the state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid cascading failures
&lt;/h2&gt;

&lt;p&gt;Unless you’ve carefully controlled all possible continuation scenarios, allowing execution by failing safe means you’re effectively allowing your system to enter unknown territory. Unexpected invalid system states lead to more invalid states, and before you know it, they cascade into a much larger failure than if you had stopped it in time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less cognitive load and simple mental models
&lt;/h2&gt;

&lt;p&gt;Failing safe means adding branches of possible code paths, resulting in more things to juggle around in your head and, consequently, a greater likelihood of mistakes.&lt;/p&gt;

&lt;p&gt;Failing fast means predictable and deliberate programming. You write code confidently when you can rest assured that the system is in the state you expect it to be. By failing fast, you’ve effectively ruled out the possibility of unexpected states, allowing you to work with a simple mental model of your system.&lt;/p&gt;

&lt;h1&gt;
  
  
  Assertive Programming
&lt;/h1&gt;

&lt;p&gt;There’s a whole software development methodology for fail-fast enthusiasts, called &lt;strong&gt;assertive programming,&lt;/strong&gt; which I first read about in &lt;em&gt;The Pragmatic Programmer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Assertive programming follows the principle of failing fast by using assertions in the code to continuously validate the system’s state, throwing (crashing) if an assertion’s criteria have not been met.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateAdult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;sellItemTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add an assertion after generating an &lt;code&gt;adult&lt;/code&gt; as a defensive check to ensure that the system will only ever get to line 3 if the statement &lt;code&gt;adult.age &amp;gt;= 18&lt;/code&gt; holds true. You might think, “but that won’t ever happen”. Well, although this example is an oversimplification, it’s always worth adding assertions to ensure that something that can’t happen, won’t.&lt;/p&gt;

&lt;p&gt;Assertive programming is a good practice to validate your assumptions as you write code, especially if it’s rather clever and prone to mistakes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;someCrazyCalculationThatReturnsAPositiveNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with num&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;someCrazyCalculationThatReturnsAPositiveNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// I don't entirely trust my crazy calc code yet&lt;/span&gt;
  &lt;span class="nx"&gt;doSomethingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ultimately, it’s intended to be a tool to increase your confidence as you code and help you build robust, fail-fast systems.&lt;/p&gt;




&lt;p&gt;I hope this post shed a bit more light on the many benefits of failing fast, and that you’ll be using this type of approach more often in your solutions. Thanks for reading, and I’d love to hear your thoughts. Feel free to connect with me on &lt;a href="https://twitter.com/denhox"&gt;Twitter&lt;/a&gt; or &lt;a href="https://linkedin.com/in/denhox"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>bestpractices</category>
      <category>failfast</category>
      <category>assertiveprogramming</category>
    </item>
    <item>
      <title>How to Join Toptal: A Detailed Strategy Guide</title>
      <dc:creator>Denat Hoxha</dc:creator>
      <pubDate>Fri, 10 Dec 2021 10:31:04 +0000</pubDate>
      <link>https://dev.to/denat/how-to-join-toptal-a-detailed-strategy-guide-779</link>
      <guid>https://dev.to/denat/how-to-join-toptal-a-detailed-strategy-guide-779</guid>
      <description>&lt;p&gt;&lt;a href="https://www.toptal.com/NWzA7Z/worlds-top-talent"&gt;Toptal&lt;/a&gt; is one of the most exclusive freelancer networks out there, claiming to accept only the top 3% of applicants. &lt;/p&gt;

&lt;p&gt;Once you're part of the network, you're part of it pretty much forever. You have the option of going for long-term full-time jobs, temporary full-time, or part-time gigs. You set your own hourly rate, but based on Toptal's price lists for companies, a good approximate is $50-$85+/hour (USD).&lt;/p&gt;

&lt;p&gt;Wanting to give the contractor life a try, I joined the platform over a year ago and have just recently logged my 2000th hour. It's been wonderful so far. The clients are professional, the pay is excellent, and working remotely as a contractor is as flexible as it gets. &lt;/p&gt;

&lt;p&gt;In this post, I'll be going through the steps of joining the platform as a developer while sharing some effective strategies, tips and tricks to maximize your chances of getting accepted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're thinking of joining, &lt;a href="https://www.toptal.com/NWzA7Z/worlds-top-talent"&gt;apply to Toptal using my affiliate link&lt;/a&gt; and we'll both get $500 ($2500 for certain skill sets) when you land your first job. Feel free to DM me &lt;a href="https://twitter.com/denhox"&gt;@denhox&lt;/a&gt; and I'll help you out however I can.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 - Brief intro and language evaluation
&lt;/h2&gt;

&lt;p&gt;Difficulty: 🧡🤍🤍🤍🤍&lt;/p&gt;

&lt;p&gt;This introductory step won't take long, about 5-10 minutes. You'll be invited to a video call where you'll give some details about your experience and what you're looking for at Toptal. The goal is to assess your English &amp;amp; communication skills. Be genuine and comfortable, this step is nothing to worry about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - Algorithmic Challenges
&lt;/h2&gt;

&lt;p&gt;Difficulty: 🧡🧡🧡🧡🤍&lt;/p&gt;

&lt;p&gt;Things get interesting from here. For your first technical assessment, you'll be given a link to complete a 90-minute coding test with 3 algorithm challenges. &lt;/p&gt;

&lt;p&gt;Some info on the challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your solutions will be assessed by their &lt;a href="https://support.codility.com/hc/en-us/articles/360043825313-Reading-a-candidate-test-report"&gt;correctness and performance&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You're not guaranteed to get the same problem set as another candidate.&lt;/li&gt;
&lt;li&gt;The acceptance criteria is quite ambiguous. From my observations, you're not required to solve all of the problems, and there is no set score threshold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no shortcuts to this step (or any of the next steps, to be fair), so a lot of it comes down to your experience in solving algorithmic challenges. Even experienced applicants will have to brush off some dust before jumping in. However, there is a clear roadmap on how to get to the level you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Familiarize yourself with the algorithm challenge format&lt;/strong&gt;. All challenges have the same structure: the problem statement, input, and expected output format. Go through the &lt;a href="https://app.codility.com/demo/take-sample-test/"&gt;Codility practice test&lt;/a&gt; to see this structure, and also familiarize yourself with the platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose a programming language you're most comfortable with&lt;/strong&gt;. Nail down its string &amp;amp; array operations, arithmetic, data structures (especially maps), and bitwise operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice on sites like &lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt;, &lt;a href="https://www.hackerrank.com/"&gt;HackerRank&lt;/a&gt;, and &lt;a href="https://www.codewars.com/"&gt;CodeWars&lt;/a&gt;&lt;/strong&gt;. You can even combine them for more variety. Here's a &lt;a href="https://www.teamblind.com/post/New-Year-Gift---Curated-List-of-Top-75-LeetCode-Questions-to-Save-Your-Time-OaM1orEU"&gt;brilliant curated list of 75 LeetCode questions&lt;/a&gt; that cover a large variety of exercises. Also, check out the &lt;a href="https://app.codility.com/programmers/lessons/1-iterations/"&gt;Codility lessons&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time yourself as you get deeper into practice&lt;/strong&gt;. The real test is timed and has the pressure factor, so you should simulate this during practice as much as you can. &lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy
&lt;/h3&gt;

&lt;p&gt;The main goal is to get the highest overall score you possibly can. &lt;/p&gt;

&lt;p&gt;The optimal way to do this is to gather as many points from all three challenges. Aim for breadth rather than depth by giving each task a fair shot, versus putting all your eggs in one basket by focusing your efforts on only one task. &lt;/p&gt;

&lt;p&gt;Therefore it stands that to achieve the most gains you want to give an equal amount of time to each task and no more. Put your full focus into one task, but once its 30 minutes are up, drop it immediately and move on to the next one. If you solve a task in less than 30 minutes, congrats! You just added some bonus time to the remaining ones. &lt;/p&gt;

&lt;p&gt;Solving a task against the test input doesn't necessarily mean your solution's correctness is going to be 100%. After submission, the platform will test your solution against some hidden "real" input, which will ultimately decide your correctness score. But don't let this bother you too much. If your solution passes the test cases and its time is almost up, move on.&lt;/p&gt;

&lt;p&gt;The key is to aim for the correctness points of all three tasks first, and only put effort into improving your algorithm's performance if there's time remaining at the end. One little obstacle in scoring for performance is that the platform won't give you any performance measurement indicators, it only judges it after your official submission (which is too late anyway). So, for performance, you'll have to figure out the algorithmic complexity yourself and decide whether it's good to go.      &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Live Coding
&lt;/h2&gt;

&lt;p&gt;Difficulty: 🧡🧡🧡🧡🧡&lt;/p&gt;

&lt;p&gt;Welcome to what I believe is the most difficult step. According to Toptal, your chances of passing this phase are 48%. Looks pretty good to me! &lt;/p&gt;

&lt;p&gt;This step is essentially a validation of your algorithm-solving skills, but by a human in a real-time video session. You'll have to solve &lt;strong&gt;two algorithm challenges, with 15 minutes each&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Have your development environment set up and ready with a barebones "Hello world" of your favorite language, ready to roll. &lt;/p&gt;

&lt;p&gt;On joining the call, the Toptal technical screener will greet you, ask you a bit about the challenges you had in completing the previous step, and then ask you to open a webpage in your browser where they'll present you with your first problem statement. &lt;/p&gt;

&lt;p&gt;You have less time to come up with a solution in this round, although the problems are of a slightly lower difficulty than the previous step. Correctness is everything and it's crucial for your solution to produce correct output. Performance does not matter as your solution won't be tested against huge input. &lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Improve your speed by incorporating timing into your practice&lt;/strong&gt;, and practice more. Use a timer for understanding the problem statement, and limit that to a maximum of 3 minutes. Then use a timer for the coding part, with a limit of 15 minutes. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice thinking out loud&lt;/strong&gt; while you're understanding the problem statement, planning your solution, and while writing your code. Verbalize your thought process. See the strategy phase below for why this is important.   &lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy
&lt;/h3&gt;

&lt;p&gt;The absolute number one tip for this step that I don't see people doing enough is to &lt;strong&gt;think out loud during the entire process&lt;/strong&gt;. Most screeners will jump in and correct any misassumptions or give you a little towards the correct solution if they notice you might be wandering off the path. They will only be able to do this if you allow them to follow your thought process, and that's why you have to verbalize every decision you're making, from analyzing the problem statement to writing the code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spend as much time as possible understanding the problem statement before starting the timer.&lt;/strong&gt; The screener won't start the timer until you say so, and you shouldn't say go until you've digested the problem statement entirely. There's an unwritten limit of course, but it's on you to prolong this as much as you need. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not make assumptions&lt;/strong&gt;. One of the biggest reasons that candidates fail this step is that they make an incorrect assumption in the problem statement, eager to jump right into the code. Ask about any part of the problem statement that is not crystal clear to you, before starting the timer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identify the problem's edge cases and say them out loud too&lt;/strong&gt;. As you might already know by now, edge cases in these algorithmic challenges represent some risky traps that require some special handling in your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Test Project
&lt;/h2&gt;

&lt;p&gt;Difficulty: 🧡🧡🧡🤍🤍&lt;/p&gt;

&lt;p&gt;You're almost at the finish line! According to Toptal, about 88% of applicants pass this phase. Looking good. 🙂&lt;/p&gt;

&lt;p&gt;You will be sent a project assignment to complete within 2 weeks. The project depends on what you applied as (backend, frontend, or full-stack), but it'll typically take up to 40 hours. Plan accordingly. &lt;/p&gt;

&lt;p&gt;The project assignment contains a list of functional and non-functional requirements that your project will have to fulfill. You'll be given access to a private Git repo to push your commits. Make sure you don't push any commits after the 2 weeks are up.&lt;/p&gt;

&lt;p&gt;When you're done, you'll be able to set up a meeting to demo your project. During the demo, the screener will ask you to go through every requirement as they check them off the list.  &lt;/p&gt;

&lt;p&gt;The biggest factor for success in this step is (surprise, surprise) the sum of your technical project experiences so far in your career. A rough prerequisite for applying to Toptal is about 3 years of experience, which you most likely have if you've made it to this point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use correct HTTP status codes and other semantics.&lt;/strong&gt; Your project likely involves an API to build, consume, or both. The technical screener will verify that you're returning correct HTTP status codes for invalid input (400), non-existent resources (404), unauthorized requests (401), insufficient permissions (402), redirects (3xx), and a catch-all server error response (500). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test extensively&lt;/strong&gt; and make sure your project is clear of bugs and unhandled exceptions. An unhandled exception during the real demo will likely be fatal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rehearse the demo at least once.&lt;/strong&gt; It's extremely likely you'll catch a missing piece of a requirement, or stumble upon an exception.    &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When the last requirement is ticked off the list, you're done! Congrats! 🎉&lt;/p&gt;

&lt;p&gt;The screener will congratulate you and set your account and Toptal email up for onboarding. You're officially part of Toptal and can now apply for jobs, which in itself is a challenge that I'll be writing about in a future post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're thinking of joining, &lt;a href="https://www.toptal.com/NWzA7Z/worlds-top-talent"&gt;apply to Toptal using my affiliate link&lt;/a&gt; and we'll both get $500 ($2500 for certain skill sets) when you land your first job. Feel free to DM me &lt;a href="https://twitter.com/denhox"&gt;@denhox&lt;/a&gt; and I'll help you out however I can.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Got any questions? Have you had a different experience when joining? Anything I might have missed? I'd appreciate it if you could let me know &lt;a href="https://twitter.com/denhox"&gt;@denhox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

</description>
      <category>toptal</category>
      <category>interviewing</category>
      <category>freelance</category>
    </item>
    <item>
      <title>Four Lessons Learned from Building Microservices</title>
      <dc:creator>Denat Hoxha</dc:creator>
      <pubDate>Mon, 01 Nov 2021 14:14:23 +0000</pubDate>
      <link>https://dev.to/denat/four-learnings-from-building-microservices-3cen</link>
      <guid>https://dev.to/denat/four-learnings-from-building-microservices-3cen</guid>
      <description>&lt;p&gt;I've had my fair share of projects based on microservices architecture. As fun as they are to work on, we fell into some nasty traps, some of which should've been painfully obvious. Here are four of them that I could gather from memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Services should hide their internal details
&lt;/h2&gt;

&lt;p&gt;A trap I fell into pretty early in my microservices journey was allowing another team's microservice to access our microservice's database directly. &lt;/p&gt;

&lt;p&gt;Their team was tasked with building a back-office internal tool capable of reading and writing data in all of the other microservices, including ours. Our public GraphQL API wouldn't suffice, it had very conservative queries and mutations intended only for our public clients. For what this team needed, we would need to add a bunch of administrational operations to our API.&lt;/p&gt;

&lt;p&gt;Amid a very tight project schedule and with us thinking it would save us a lot of time, we proceeded with having their service access our service's DB directly. Yikes.&lt;/p&gt;

&lt;p&gt;Not long after that, we realized that we had effectively given up our freedom to make changes to our database's schema, because the back-office tool had now been hard-coupled to it. &lt;/p&gt;

&lt;p&gt;Getting things right was painful, but eventually we got their microservice's read paths to go through our API, and cut off their dependency to our database.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we learned
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Don't break a microservice's boundary&lt;/strong&gt; by allowing another service to directly access one of its components (e.g. the DB). This covertly introduces a new contract that it'll have to adhere to. A microservice should (aim to) uphold one contract only: its API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A microservice should hide as much details as possible.&lt;/strong&gt; You can only easily change components that are hidden from the outside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invest early in the infrastructure required for service-to-service communication,&lt;/strong&gt; i.e. service discovery and machine-to-machine tokens. You'll need it eventually, and this was a major factor that drove us away from the correct solution because the amount of work seemed significant. &lt;/p&gt;

&lt;h2&gt;
  
  
  Chatty microservices likely belong together
&lt;/h2&gt;

&lt;p&gt;On what should've been a boring Tuesday, some QA reports began storming in that the application was hanging on certain screens. We confirmed that on our observability tool, Instana, where we saw a couple of GraphQL queries taking &amp;gt;30 seconds then ultimately failing. &lt;/p&gt;

&lt;p&gt;The distributed trace in Instana revealed an infinite loop of requests as a result of a cyclic dependency between two of our services. We located the bug which was introduced in a recent commit: a new piece of middleware in service A was making a request to service B, which then would make a request to service A, triggering service A's middleware again to fire off a request to service B, and so on. &lt;/p&gt;

&lt;p&gt;We realized we had been lucky to not run into this issue for as long as we did, considering that these two services were already very chatty and had pretty much always been dependent on each other.&lt;/p&gt;

&lt;p&gt;As an urgent fix, we reverted the commit and decided to merge the two services into one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we learned
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Observability tools and distributed tracing save the day.&lt;/strong&gt; The long alternating sequence of requests in the request trace let us instantly recognize there was a cyclic dependency issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two services being very chatty is a good indicator that they belong together.&lt;/strong&gt; Most observability tools provide visuals of intra-service communication in your ecosystem that can help you spot which services are communicating excessively. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best way to avoid cyclic dependency issues is to not have cyclic dependencies.&lt;/strong&gt; Shallow cyclic dependencies like in our case (A → B → A) are manageable to a certain degree, but a poor design could end up introducing infinite request loops from transitive circular dependencies like A → B → C → A. Aim for no cyclic dependencies and a unidirectional request flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimize alerting noise.&lt;/strong&gt; Ideally, an issue like this should have been noticed by us via our monitoring alerts, long before anyone had reported it. We had a Slack channel set up for automated monitoring alerts, but there was too much noise going on in there that we had gotten used to ignoring it. This incident made us take the time to revamp the alerts and be more attentive to the alert channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintain independent deployability
&lt;/h2&gt;

&lt;p&gt;One of the core principles of microservices is &lt;strong&gt;independent deployability&lt;/strong&gt;. You should be able to deploy a microservice at any time, without having to deploy anything else. Deviating from this rule brings trouble.&lt;/p&gt;

&lt;p&gt;In one of my past teams, we had a tradition to gather once a week in a "war room" for a mass deployment of a usual set of 6-7 microservices to launch new features. To minimize downtime, these services had to be released in a strictly ordered sequence.&lt;/p&gt;

&lt;p&gt;The biggest reason for these coordinated deployments was that teams were allowed to break their API contracts. There were no rules in place to require backward compatibility for every service. We assumed this just came with the territory, accepting it as a normal part of practicing microservices. However, over time the major downsides became very apparent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Jnct-iv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eswr74hbpr6fx3gjo83t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Jnct-iv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eswr74hbpr6fx3gjo83t.png" alt="https://sebiwi.github.io/comics/distributed-monolith/" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we had messed up one of the services and couldn't push a hotfix quick, we had to revert &lt;em&gt;all&lt;/em&gt; the deployments. This was frustrating.&lt;/p&gt;

&lt;p&gt;Allowing microservices to disregard backward compatibility effectively lowered our confidence in the stability of the ecosystem. &lt;/p&gt;

&lt;p&gt;Getting many people to sync for the coordinated deployment was difficult since microservice teams usually had their own plan schedules. Also, any hiccup or failure during the process meant a lot of wasted time and productivity.&lt;/p&gt;

&lt;p&gt;And, ultimately, it never felt right that teams had to concede their autonomy of deploying their microservices to production.&lt;/p&gt;

&lt;p&gt;One of many things we did to address these was to add a rule: microservices must maintain backward compatibility and not break their API contracts. If breaking changes are inevitable, teams must be notified ahead of time and API features must go through a deprecation phase first. &lt;/p&gt;

&lt;h3&gt;
  
  
  What else we learned
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Integrate automated checks in the CI/CD pipeline to report and prohibit breaking changes.&lt;/strong&gt; We added a step to our pipeline that used static analysis to compare the new API contract to the previous one. Additive changes are OK, but any removal or modification of an API method failed the CI/CD job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A culture of maintaining backward compatibility&lt;/strong&gt; did wonders for our sanity and confidence in the system, knowing that you won't wake up one morning to find out a microservice's endpoint is now returning a completely new format or even disappeared completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature flags are a very useful tool to separate code deployment from feature deployment.&lt;/strong&gt; If you want to deploy a breaking change, you can first hide it behind a feature flag, deploy the code, then switch the feature flag on at your convenience. More on feature flags below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding end-to-end tests has a point of diminishing returns
&lt;/h2&gt;

&lt;p&gt;The above statement admittedly applies to most systems regardless of architecture, but it was especially evident in a microservices project I took part in that had very healthy test coverage. The React frontend was full of integration and Cypress end-to-end tests, whereas the backend services had their own unit and end-to-end tests. &lt;/p&gt;

&lt;p&gt;After a certain point, the number of bugs that made it through to staging and production began growing linearly with the number of new features added, even though we had strict measures in place that no code was to get merged without its accompanying tests. &lt;/p&gt;

&lt;p&gt;We needed a strategy to complement the tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we did (and learned)
&lt;/h3&gt;

&lt;p&gt;The answer was &lt;strong&gt;testing in production&lt;/strong&gt;, as so nicely explained in &lt;a href="https://copyconstruct.medium.com/testing-in-production-the-safe-way-18ca102d0ef1"&gt;Cindy Sridharan's "Testing in Production, the safe way"&lt;/a&gt;. This strategy involves applying &lt;em&gt;post-release&lt;/em&gt; techniques to tackle encountered issues, rather than trying to prevent them all pre-release.&lt;/p&gt;

&lt;p&gt;What this meant for us, was to crank up our monitoring and work on our knowledge sharing regarding our distributed tracing tools, making sure every developer can find their way around it.&lt;/p&gt;

&lt;p&gt;But one of the techniques that proved most effective for us was the usage of &lt;strong&gt;feature flags&lt;/strong&gt;. New feature code was expected to be wrapped in a feature flag, which we would then use to roll the feature out gradually, starting from our small team to the whole company, and finally to the entire userbase (we've had great success with &lt;a href="https://launchdarkly.com/"&gt;LaunchDarkly&lt;/a&gt;). We piggybacked on this mechanism for error resolution as well: when our monitoring tools reported critical errors that originated from a particular feature, we could easily switch the feature off to buy us some time to understand the problem and apply a hotfix. This is a great example of how we did testing in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endnotes
&lt;/h2&gt;

&lt;p&gt;Some of the principles and rules I've mentioned above are brilliantly explained in the book &lt;em&gt;Building Microservices&lt;/em&gt; by Sam Newman, which I've had the chance to read recently. It's a great resource with principles and best practices that will save you from the &lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing"&gt;many pitfalls of microservices&lt;/a&gt;.  The &lt;a href="https://samnewman.io/books/building_microservices_2nd_edition/"&gt;second edition&lt;/a&gt; has been released and I highly recommend it!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
