<?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: Diogo Daniel Soares Ferreira</title>
    <description>The latest articles on DEV Community by Diogo Daniel Soares Ferreira (@diogodanielsoaresferreira).</description>
    <link>https://dev.to/diogodanielsoaresferreira</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%2F244048%2Ffefa0af6-4077-4b10-8ac2-18203228c973.jpg</url>
      <title>DEV Community: Diogo Daniel Soares Ferreira</title>
      <link>https://dev.to/diogodanielsoaresferreira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/diogodanielsoaresferreira"/>
    <language>en</language>
    <item>
      <title>Can Your LinkedIn Inbox Predict the Job Market?</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Thu, 19 Mar 2026 18:01:33 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/can-your-linkedin-inbox-predict-the-job-market-1lok</link>
      <guid>https://dev.to/diogodanielsoaresferreira/can-your-linkedin-inbox-predict-the-job-market-1lok</guid>
      <description>&lt;p&gt;Hi there! Recently I noticed that I was receiving more LinkedIn messages than usual. Have you also been receiving more messages from recruiters on LinkedIn recently, or is it just me? Is the tech job market heating up again?&lt;/p&gt;

&lt;p&gt;To answer this question, I tracked every recruiter message since 2020. Here's the number of messages I received each month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfly9jw69t3zmfs8vbzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfly9jw69t3zmfs8vbzl.png" alt="Number of messages received on LinkedIn from January 2020 to February 2026" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;
Number of messages received on LinkedIn from January 2020 to February 2026.



&lt;p&gt;I started working in September 2019, so this dataset covers my career from 5 months of experience to more than 6 years. In total, it contains 544 recruiter messages over 6 years.&lt;/p&gt;

&lt;p&gt;A few things stand out. There is a peak around mid-2022, which matches the hiring boom across the tech industry, followed by the crash in 2023.&lt;br&gt;
In December 2023 I moved from Portugal to Belgium, which might explain the drop in messages in the following months.&lt;/p&gt;

&lt;p&gt;In the last month we also observe an uptick in the number of messages compared with the previous months.&lt;/p&gt;

&lt;p&gt;Can the number of LinkedIn messages be used as a proxy for the market? Let's look at the software development job postings data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuuxwt2skbfvphjtbb50g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuuxwt2skbfvphjtbb50g.png" alt="Software Development Job Postings in several countries calculated as percentage change from January 2020." width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;
Software Development Job Postings in several countries calculated as percentage change from January 2020. Source: Indeed, Software Development Job Postings (via FRED, St. Louis Fed)



&lt;p&gt;The similarities are pretty remarkable. The trends in LinkedIn messages look very similar to the trends in job postings. Let's overlay the charts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer2z4jwhlc8c47capqp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer2z4jwhlc8c47capqp9.png" alt="Software Development Job Postings in several countries calculated as percentage change from January 2020 and Number of messages received on LinkedIn from January 2020 to February 2026." width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The peak in the number of LinkedIn messages appears a few months after the peak in job postings, which makes sense, since companies first open roles, and only later start actively reaching out. The most recent peak is also more noticeable in the LinkedIn messages than in the market data, but the upward trend is still visible for most markets.&lt;/p&gt;

&lt;p&gt;At least in my case, the LinkedIn inbox can be a surprisingly good indicator of the software job market. While this is just a single data point and depends on factors like experience, location, and tech stack, the correlation with broader job postings data is hard to ignore.&lt;/p&gt;

&lt;p&gt;The next time you are looking for a new job, it might be a good idea to check your LinkedIn inbox to understand if it's a good time to apply or if it might be worth waiting a bit for the market to improve.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>career</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>On Building a Platform from zero</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Sun, 08 Feb 2026 13:04:12 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/on-building-a-platform-from-zero-45aa</link>
      <guid>https://dev.to/diogodanielsoaresferreira/on-building-a-platform-from-zero-45aa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsj3viynw1pdmq8vxd2t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsj3viynw1pdmq8vxd2t.jpg" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@davealmine?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Dawid Zawiła&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/bare-tree-emerging-from-soft-morning-mist-hbUh0mnK7Tw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;At Timefold we are building a platform for everyone to use planning models through a REST API. &lt;strong&gt;Building a software platform from the ground up&lt;/strong&gt; is something thas has always fascinated me. It's amazing to see the progress done month after month in terms of scalability, performance and reliability. It's also interesting to look back and see where we failed, where we had unecessary complexity or when our assumptions were wrong.&lt;/p&gt;

&lt;p&gt;Over these (more than) two years, here are &lt;strong&gt;some of my learnings&lt;/strong&gt; on building a platform, in no particular order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging, Metrics &amp;amp; Alarms from day one
&lt;/h2&gt;

&lt;p&gt;Logging, metrics and alarms are crucial to know what is happenning in the platform. You don't want to deploy a platform to production where you have no insight into it, behaving like a black box. To be able to improve its performance and reliability &lt;strong&gt;you need to measure and understand the behaviour of your platform&lt;/strong&gt;. To define what metrics are important to track, think of the main use cases that your platform supports. As you improve your platform over time, add logging, metrics and alarms where you think you don't have enough observability, for example when your users report a bug before you can detect it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit logs for every operation done by a user
&lt;/h2&gt;

&lt;p&gt;A useful feature to have in a platform it's an audit log. For every relevant action in the platform, it &lt;strong&gt;records the identity of the requesting user along with the request parameters&lt;/strong&gt;. With an reliable audit log you can reconstruct the operations that led the platform to become in a specific state. This can be important from a security perspective, to identify malicious actors in your system, but also from a debugging perspective, to reproduce bugs reported by clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it simple
&lt;/h2&gt;

&lt;p&gt;If there is a simple way to do something, that's the preferred approach to do it. &lt;strong&gt;Don't overcomplicate your architecture&lt;/strong&gt;. This will make your platform easier to use, better adapted to the user needs and easier to modify. When building a platform from scratch, there are a lot of features that will be changed or even removed. By building a simple architecture, you will be saving yourself effort later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the monolith as long as you can
&lt;/h2&gt;

&lt;p&gt;This is a corollary of the previous point, but I decided to adress it separately because it's a common mistake (I certainly made it before). There are good reasons to split a single service into multiple services, such as different scaling requirements or if they are managed by different teams. However, if you don't have those requirements, don't do it. &lt;strong&gt;Separate services have their own headaches&lt;/strong&gt;. With more moving parts comes harder debugging, increased latency and more complex coordination. A good rule of thumb is to keep the monolith for as long as you can and only split it if you really have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cloud services to offload your team
&lt;/h2&gt;

&lt;p&gt;It can be tempting to build everything ourselves (tip: don't reinvent the wheel!). It can also be tempting to host everything on-prem because it will be cheaper. However, nowadays many platform teams are small and are focused on shipping fast. The more components a team maintains (eg. storage, message queues, etc.), the more it will slow them down, and the more incidents they will have. &lt;strong&gt;The solution is to rely on platforms as a service&lt;/strong&gt;. Yes, the visible cost may be higher than on-prem, but in the long-term (usually) the most expensive item of your platform is not the platform itself, but the engineers that maintain it. If your team can be focused on developing their own platform and not on maintaining external services, the total platform cost will almost always be less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document (almost) everything
&lt;/h2&gt;

&lt;p&gt;Document, document, document. &lt;strong&gt;Every team decision should be documented in a shared space&lt;/strong&gt;. This includes code decisions (why is the code structured as is?), architectural decisions (why are we using service x instead of y), or product decisions (why is the API done like this?). Documenting everything is essential because as team elements come and go, context can be kept within the team and not lost. Even if the team elements do not change, the reason of many decisions will be forgotten in a few months if not written down. There are many ways to document decisions - Git repository, Confluence, Shared folder in Google Drice, Slack - any that fit your workflow is good enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be careful about cloud vendors lock-in
&lt;/h2&gt;

&lt;p&gt;It's easy to start using a cloud vendor (eg. AWS, Google Cloud or Azure) and to use their services. However, &lt;strong&gt;it's not wise to lock yourself into a specific vendor&lt;/strong&gt;. If their price goes up, their service becomes degraded, or by any other reason, you may want to switch from cloud provider. Trying to keep an independent approach towards cloud vendors is smart. If you need a specific service from them, you can always build an adaptor for your application that uses that service and that can be switched later to any other vendor, if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the economics of your application
&lt;/h2&gt;

&lt;p&gt;How are users paying for your platform? Is it per time spent in the platform? Is it with a subscription? Is it a one-time payment? All these questions are relevant for you to build the architecture of your platform. After all, you don't want to build a platform that costs more to run than what the users are paying for it. &lt;strong&gt;Keep track of the cost of running your platform&lt;/strong&gt; (per user, if possible). Is there a way to make it run cheaper without degrading the service?&lt;/p&gt;

&lt;h2&gt;
  
  
  Have users and gather feedback as early as possible
&lt;/h2&gt;

&lt;p&gt;When building a platform from zero, you will have many assumptions about how users will use the platform, and you will build the architecture around it. But many of those assumptions will inevitably be wrong, and you will discover it once you start to see users interacting with it. Having that feedback as early as possible &lt;strong&gt;helps you to course-correct and fix possible issues&lt;/strong&gt; with the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the bigger picture
&lt;/h2&gt;

&lt;p&gt;I have to admit that this was my biggest mistake at the beginning (and of many junior engineers). Sometimes I was so focused on doing my task that I didn't ask why it was needed, and if it could be done in a better way. This is actually one of the key differences between a junior and a more experienced engineer: while the junior engineer is focused on doing its task well, &lt;strong&gt;the experienced engineer is able to see the bigger picture beyond the task, understand the importance of each task in the context of the platform, and above all is able to ask the right questions&lt;/strong&gt;. If you can understand the reason behind each task and how it correlates with the platform vision, you'll be able to work much faster and reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  An experienced team gives you a jump-start
&lt;/h2&gt;

&lt;p&gt;Team members that have already built platforms from scratch can be the difference between building a useful platform or building an MVP that will have to be rebuilt once users start interacting with it. &lt;br&gt;
That’s because &lt;strong&gt;experienced engineers have already made mistakes in the past and know how to avoid them&lt;/strong&gt;. They may be more expensive to hire, but they will pay for themselves in the long term.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>careerdevelopment</category>
      <category>programming</category>
    </item>
    <item>
      <title>Choosing Your Next Job: Startup vs Large Company, B2B vs B2C</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Sat, 03 Jan 2026 22:13:11 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/choosing-your-next-job-startup-vs-large-company-b2b-vs-b2c-4n6f</link>
      <guid>https://dev.to/diogodanielsoaresferreira/choosing-your-next-job-startup-vs-large-company-b2b-vs-b2c-4n6f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18siuioop8q9f8bzi33f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18siuioop8q9f8bzi33f.jpg" width="420" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi there! A lot has already been written about the differences between working at a startup and working at a large company. However, there is another important difference that is often disregarded by candidates, but that is nonetheless very important: &lt;strong&gt;Business-to-Business (B2B)&lt;/strong&gt; vs &lt;strong&gt;Business-to-Customer (B2C)&lt;/strong&gt;. The business model of a company can be as important for your decision of your next workplace as the size of it.&lt;/p&gt;

&lt;p&gt;Each company is obviously different from each other, and there are exception to all rules. However, depending on its size and the type of clients they have, it's possible to identify some patterns. Having worked across some of these environments, a few things stand out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Startups vs Large companies
&lt;/h2&gt;

&lt;p&gt;Let's start by analyzing the differences between working at startups and working at large companies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Work-life balance
&lt;/h3&gt;

&lt;p&gt;When working at a startup, it's well known that the work-life balance can generally be more &lt;strong&gt;volatile&lt;/strong&gt; than working at a large company. Usually, the smaller the company, the more &lt;strong&gt;stressful and energy-draining&lt;/strong&gt; it can be. If there is a problem, you will probably be the one who gets called, while at a large company, there are many teams that can get called to solve a problem.&lt;br&gt;
At a startup, the development cycles are also shorter, which means faster development, faster and more often deployments, but less time for testing. At a large company, fewer deployments are the norm, but the testing of each one is essential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Autonomy, Responsibility &amp;amp; Visibility
&lt;/h3&gt;

&lt;p&gt;At a startup, you will usually have more &lt;strong&gt;freedom, autonomy and responsibility&lt;/strong&gt; than in a large company. You are much more &lt;strong&gt;visible&lt;/strong&gt;, which means that your impact (and mistakes) are more obvious.&lt;br&gt;
At a large company you will have less freedom and responsibility, because many of the decisions have already been made by someone up in the hierarchy. You will also have less visibility, which is bad if you want to stand out, but good if you want to blend in. Many developers report the feeling of being "just a cog in the wheel" in large companies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Engineering Process &amp;amp; Quality Bar
&lt;/h3&gt;

&lt;p&gt;While there is no hard rule about which type of companies produces better code, in startups there are usually fewer safeguards (fewer tests and verifications), as &lt;strong&gt;shipping fast is prioritized over reliability&lt;/strong&gt;. This can sometimes results in large tech debt. The products are also &lt;strong&gt;less stable&lt;/strong&gt;, as there are fewer clients (or sometimes no clients at all).&lt;br&gt;
In large companies, usually with slower development, there are more reviews, checks and verifications, and there is a stronger emphasis on reliability and long-term stability. You will usually be maintaining or iterating on already existing products, with good practices in place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning
&lt;/h3&gt;

&lt;p&gt;In startups, learning depends much on the other members of the team. &lt;strong&gt;Mentorship&lt;/strong&gt; can be limited because everyone is focused on building, and it may be the case that most or all team members are not very experienced. Regardless, you will learn a lot by doing, because usually you will have do to a bit of everything. You may need to be a &lt;strong&gt;jack-of-all-trades&lt;/strong&gt;, since there is a lot to do and not many employees who can help you. You may also have the opportunity to build something from scratch, which is rarer in large companies.&lt;br&gt;
In large companies, there is usually more guidance from other senior colleagues, as well as better onboarding and mentorship, but you will probably not work across many different parts of the overall architecture, as there most likely are teams focused on different services. You may turn into a specialist in a specific area (depth instead of breadth).&lt;/p&gt;




&lt;h2&gt;
  
  
  B2B vs B2C
&lt;/h2&gt;

&lt;p&gt;A company with a B2B business model sells products to other companies. Salesforce, Oracle or SAP are some example. Companies with a B2C business model sell products directly to clients. Examples of such companies are Meta, Google and Apple.&lt;/p&gt;

&lt;p&gt;But what are the differences between working at a B2B company and working at a B2C company?&lt;/p&gt;

&lt;h3&gt;
  
  
  Business Stability
&lt;/h3&gt;

&lt;p&gt;B2B companies usually have a more predictable revenue stream, so they are considered &lt;strong&gt;more stable&lt;/strong&gt;. Once customers sign, there is much less existential panic. The sales cycles here can be long but very important. The &lt;strong&gt;iteration cycles are usually longer&lt;/strong&gt; when compared with B2C companies, and the changes are often coordinated. &lt;strong&gt;Backward compatibility&lt;/strong&gt; is important.&lt;br&gt;
When you work directly for the clients (B2C), there is a greater probability that the traffic or revenue can drop suddenly, which requires more adaptability and sudden changes of plans. This makes it so that &lt;strong&gt;the pace is faster&lt;/strong&gt; than most B2B companies, with faster iteration loops. Launch weeks can be specially &lt;strong&gt;intense and stressful&lt;/strong&gt;, because there can be a high number of users stress-testing the product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Proximity
&lt;/h3&gt;

&lt;p&gt;B2B companies tend to work much &lt;strong&gt;closer to customer problems&lt;/strong&gt;. Their feedback is also more direct, which allows to &lt;strong&gt;change the priorities easily&lt;/strong&gt; according to their needs. The product is more often shaped by sales and customer success, which means that &lt;strong&gt;roadmaps are mainly externally-driven&lt;/strong&gt;. One downside of this is that the needs of just one important client can significantly change priorities, which will affect all other clients. If that client needs a specific feature, even if no other customer wants it, you may have to build it either way.&lt;br&gt;
In B2C companies, the feedback is much more &lt;strong&gt;indirect and data-driven&lt;/strong&gt;. Given that users must usually be anonymous, it's common that the feedback is gathered by observing &lt;strong&gt;metrics&lt;/strong&gt; like clicks on a website, number of users that follows a specific workflow, etc. &lt;strong&gt;Data is central&lt;/strong&gt; here: A/B testing, funnels or growth loops are common techniques to gather more usage data from the users. As decisions are heavily metric-driven, observability into user behavior is essential. An individual user usually does not have the power to influence the direction of the product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Engineering Focus
&lt;/h3&gt;

&lt;p&gt;At B2B companies, &lt;strong&gt;integrations&lt;/strong&gt; are very important. The clients usually already have other software and they want to integrate your own into their workflow, so APIs and custom setups per customer can be important.&lt;br&gt;
At B2C companies, on the other hand, &lt;strong&gt;performance testing&lt;/strong&gt; is critical. Since you don't know how many clients you will have, you need to &lt;strong&gt;scale your architecture&lt;/strong&gt; accordingly if a sudden spike in users occurs. For B2C, &lt;strong&gt;on-call also tends to be more stressful&lt;/strong&gt; due to large user bases and unexpected peak times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product ownership &amp;amp; Responsibility
&lt;/h3&gt;

&lt;p&gt;At B2B companies, features tend to live for years. It can be &lt;strong&gt;hard and risky to remove or change&lt;/strong&gt; them, because they are used by large enterprise users that also don't want those features to change. There is a strong expectation of &lt;strong&gt;long-term support and maintenance&lt;/strong&gt;. This can create technical and product debt over time.&lt;br&gt;
At B2C companies, it's much easier to replace or create new features, as those are much more short-lived. &lt;strong&gt;Feature deprecation&lt;/strong&gt; is expected, and there is &lt;strong&gt;less long-term commitment&lt;/strong&gt; to individual product decisions. The responsibility is much more about current performance than about historical decisions. This also means that, when working for a B2C company, it's more likely that you will end up creating new features instead of just maintaining old ones.&lt;/p&gt;

&lt;p&gt;There’s no universally better environment to work in, what matters is which one makes sense for you.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>career</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>10 Tips for Making Better Decisions</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Wed, 29 Oct 2025 18:35:52 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/10-tips-for-making-better-decisions-hp4</link>
      <guid>https://dev.to/diogodanielsoaresferreira/10-tips-for-making-better-decisions-hp4</guid>
      <description>&lt;p&gt;Hi there! AI is shrinking the amount of code many of us write. The real leverage of a software engineer, more than knowing a specific programming language or framework, comes from &lt;strong&gt;making the right decisions&lt;/strong&gt;. Not only in software engineering, but also in life: as information is more readily available than ever before, the barrier to making good decisions isn't about having access to data, but about knowing how to process it. However, every day we make dozens of calls with bias-prone brains. &lt;strong&gt;How can we make better decisions, not only as software engineers, but in our lives?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, I give 10 tips that work for me when I have to make a difficult decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start by slowing down
&lt;/h3&gt;

&lt;p&gt;The first response to a situation is usually discomfort and a sense of urgency. Avoiding to take a rushed decision and sleeping on it, or taking days or even months (if the decision is important enough) to really think it through, it's usually a good idea. Have you ever been stuck with a problem for a while and the solution only came when you were in the shower, or walking the dog, or right before you fall asleep? You're not alone.&lt;/p&gt;

&lt;p&gt;This effect is called &lt;a href="https://en.wikipedia.org/wiki/Incubation_(psychology)" rel="noopener noreferrer"&gt;Incubation&lt;/a&gt;, and it's widely acknowledged that taking a break from a problem can actually help solve it. &lt;strong&gt;The right decision often surfaces naturally&lt;/strong&gt; when the brain is not worried about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Is doing nothing an option?
&lt;/h3&gt;

&lt;p&gt;Has it ever happened to you that after a few days of thinking through the problem, you realize that &lt;strong&gt;the best decision is actually not to do anything?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is related with the principle &lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it" rel="noopener noreferrer"&gt;"You aren't gonna need it" (YAGNI)&lt;/a&gt;. Knowing the system architecture well enough to avoid building what won't deliver value, instead of building just because you can, is a sign of deep understanding.&lt;/p&gt;

&lt;p&gt;As the &lt;em&gt;Zen of Python&lt;/em&gt; beautifully puts it: &lt;strong&gt;"Now is better than never. Although never is often better than right now&lt;/strong&gt;". Patience is powerful, but passivity isn’t. The art lies in knowing when doing nothing is a wise choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Avoid the sunk cost fallacy by asking what changed
&lt;/h3&gt;

&lt;p&gt;Imagine you’re maintaining a large codebase with an old authentication module. It’s poorly designed, hard to test and every new feature requires hacks to keep it running. The team has already spent months refactoring it, but it’s still fragile. Then someone proposes: “Why don’t we just replace it with an off-the-shelf identity provider like Auth0 or Keycloak?”&lt;/p&gt;

&lt;p&gt;What do you do? The rational move is to ask: “What’s the best decision going forward, &lt;strong&gt;regardless of what we’ve already did?&lt;/strong&gt;” If you think “But we’ve already spent so much time improving it!”, you're falling into the &lt;a href="https://en.wikipedia.org/wiki/Sunk_cost_fallacy" rel="noopener noreferrer"&gt;sunk cost fallacy&lt;/a&gt; effect.&lt;/p&gt;

&lt;p&gt;If you feel compelled to undo a past choice, remind yourself that the previous decision was made with different knowledge, risk tolerance and priorities. Revisiting those decisions without judgment lets you spot when the context has shifted. Re-deciding is a sign of learning, not failure. Naming what changed helps you &lt;strong&gt;avoid the sunk cost fallacy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Focus on the long-term - The regret minimization framework
&lt;/h3&gt;

&lt;p&gt;Oftentimes, we create a story in our heads to justify the decision we want. However, it's also important to frame it from different angles, so that we understand all possible consequences. We will often find conflicting stories that make sense. How to make a decision then?&lt;/p&gt;

&lt;p&gt;Jeff Bezos was also confronted with a similar situation and created the &lt;strong&gt;Regret Minimization Framework&lt;/strong&gt;, which is quite simple: Imagine yourself at 80 years old. Will you regret taking (or not taking) the decision?&lt;/p&gt;

&lt;p&gt;As engineers, we’re often tempted by quick fixes or shortcuts that make life easier today but harder tomorrow. Thinking long-term means valuing maintainability over immediacy. Cutting corners might feel efficient now, but it often becomes technical debt later.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Listen to advice but make your own decision
&lt;/h3&gt;

&lt;p&gt;There are always people that have faced similar problems, or that have more experience than you. You can leverage them: their input is valuable. &lt;strong&gt;Don't be afraid to ask for help&lt;/strong&gt;. Engineers often want to do things alone, in a DIY way, and tend to think they are smarter than everyone else. However, it's by listening to others that we can understand the consequences of our decisions.&lt;/p&gt;

&lt;p&gt;Don't forget that &lt;strong&gt;the decision can only be made by yourself&lt;/strong&gt;. It's okay to make a decision that goes against others’ advice as long as you are aware of the risks and the consequences, because you're the one who will have to deal with them.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Make the decision reversible
&lt;/h3&gt;

&lt;p&gt;Any decision can be wrong, and it's important to be aware that any decision done today may be the wrong decision with tomorrow's knowledge. When that happens, it's important to reverse the decision. However, we can only do that if the decision is reversible.&lt;/p&gt;

&lt;p&gt;Jeff Bezos's concept of one-way door versus two-way door decisions is a useful way to frame it: &lt;strong&gt;if a decision is reversible (two-way door), it can be taken much more easily than a one-way door decision&lt;/strong&gt;. Thus, it's important to convert one-way decisions into two-way decisions whenever possible.&lt;/p&gt;

&lt;p&gt;For example, when choosing your tech stack, try to avoid vendor lock-in by &lt;strong&gt;using open standards or open-source technologies&lt;/strong&gt;. Similarly, when redesigning a system, start with an isolated service or feature flag instead of rewriting the entire codebase at once. In that way, you can revert or adjust if something breaks. Thinking in terms of reversibility frees yourself from perfectionism.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. You can't control everything
&lt;/h3&gt;

&lt;p&gt;Even with perfectly reasoned decisions, you &lt;strong&gt;can't control every variable&lt;/strong&gt;. Systems surprise us. Markets shift. Governments and policies change. Companies go bankrupt. Doing the homework reduces risk, but it does not eliminate it.&lt;/p&gt;

&lt;p&gt;For instance, you might choose a cloud provider that later changes its pricing model, breaking your cost assumptions. You might build on a stable open-source library, only to see it become unmaintained. You might optimize a product for one market segment, and then watch that market vanish because of a regulation change. None of these are signs of poor decision-making; they’re reminders that uncertainty is baked into reality. Good engineering isn’t about predicting the future — it’s about &lt;strong&gt;staying flexible when the future surprises you&lt;/strong&gt;. That mindset will keep you curious instead of paralyzed.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Trust your intuition
&lt;/h3&gt;

&lt;p&gt;When you have everything mapped out, you know the pros and cons of every option, listened to advice, reflected deeply and still can't make a final decision, what to do? &lt;strong&gt;Trust your gut.&lt;/strong&gt; Intuition is not magic, it’s &lt;strong&gt;learning from experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If everything still feels uncertain, pay attention to how your body reacts when imagining each outcome. Maybe that can give you the final hint to take your decision. The best decisions come when analysis and intuition work together: reason gives structure, intuition gives direction. When both align, you usually know it’s time to act.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Document your decision
&lt;/h3&gt;

&lt;p&gt;Who has never wanted to roll back a previous decision, but forgot why they made it in the first place?&lt;/p&gt;

&lt;p&gt;In a software engineering team, this is even more common with new elements. A new person coming on to a project may be surprised by some past decision. Without understanding the rationale or consequences, this person can only blindly accept the decision or blindly change it.&lt;/p&gt;

&lt;p&gt;To avoid this situation, once you've made your decision, &lt;strong&gt;write down why you did it&lt;/strong&gt;: the context, assumptions, risks, options and final decision (for example, in an &lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/architect-role/architecture-decision-record" rel="noopener noreferrer"&gt;Architectural Decision Record&lt;/a&gt; format). Whenever you have doubts about why you have made that decision, you can &lt;strong&gt;revisit the document that explains the context and the reasoning behind your choice&lt;/strong&gt;. If any of the assumptions or context change, it may be time to review it.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Move forward with kindness and treat outcomes as learning material
&lt;/h3&gt;

&lt;p&gt;If your decision goes south, don't beat yourself up. Practicing compassion with yourself keeps you curious instead of defensive. Don't forget that &lt;strong&gt;bad decisions are part of your journey too&lt;/strong&gt;, and are what help us learn and make better decisions in the future. &lt;strong&gt;Take something positive out of the experience&lt;/strong&gt;, maybe write a blog post about it. Extracting the lesson turns a painful outcome into future wins.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Give your AI agent superpowers with MCP</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Mon, 21 Jul 2025 09:03:37 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/give-your-ai-agent-superpowers-with-mcp-2d4l</link>
      <guid>https://dev.to/diogodanielsoaresferreira/give-your-ai-agent-superpowers-with-mcp-2d4l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nbftxl2cqm2ijfz65jt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nbftxl2cqm2ijfz65jt.jpg" alt="Abstract image that shows blue points connected by lines" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@choys_?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Conny Schneider&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-blue-background-with-lines-and-dots-xuTJZ7uD7PI?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;Hi there! These are exciting times with the industry-wide adoption of &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;. But what is MCP? And where did it come from?&lt;/p&gt;

&lt;h2&gt;
  
  
  The limits of LLMs
&lt;/h2&gt;

&lt;p&gt;With the growing usage of AI agents, there is a need to integrate with external data and tools. Without access to the outside world, an agent is limited to operating within its confined environment, usually generating text, images or similar outputs.&lt;/p&gt;

&lt;p&gt;In the early days of LLMs (Large Language Models), it was common to see them write &lt;strong&gt;"I don't have access to real-time information"&lt;/strong&gt;. These models were trained on static datasets and didn't have access to anything beyond their training data. What if you wanted your agent to close the garage door, or to send an email? The agent couldn't do it, because it had no access to the outside world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Providing AI agents with external tools
&lt;/h2&gt;

&lt;p&gt;To work around this limitation, developers began building custom tools. Frameworks like LangChain or LlamaIndex introduced the concept of &lt;strong&gt;Tools&lt;/strong&gt;, which provided a structured way to for models to invoke external functions. This meant creating a custom integration for each model-tool combination. However, each tool still had to be implemented manually and depended heavily of the model being used.&lt;/p&gt;

&lt;p&gt;Things improved with the introduction of &lt;strong&gt;RAG (Retrieval Augmented Generation)&lt;/strong&gt;, which is a technique to retrieve and incorporate new external information into the prompt context. The model could now access up-to-date information, but it could not act on it, as it was essentially read-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing MCP
&lt;/h2&gt;

&lt;p&gt;To solve this problem, Anthropic introduced the MCP as an open stardard in November 2024. &lt;strong&gt;MCP allows models to access tools and resources&lt;/strong&gt; in an unified and consistent way. Developers can now expose their tools without worrying about which model will use them, while AI agents can discover available tools and choose the most appropriate one for their task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm30uc46bdw9b4k8962yx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm30uc46bdw9b4k8962yx.png" alt="Before and after MCP" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;
Image by &lt;a href="https://www.philschmid.de/mcp-introduction" rel="noopener noreferrer"&gt;philschmid&lt;/a&gt;



&lt;p&gt;MCP follows a client-server architecture. MCP Hosts are typically AI applications (Claude Desktop, Cursor, etc.) that have a client that connects to an MCP server. The server provides the AI applications context and capabilities, such as external tools (for example, to access an API or a database).&lt;/p&gt;

&lt;p&gt;There are mainly two ways of communicating between the client and the server: &lt;strong&gt;Stdio (Standard Input/Output)&lt;/strong&gt;, used when the client and the server run on the same machine, and &lt;strong&gt;SSE (Server-Sent Events)&lt;/strong&gt;, used when they connect over HTTP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmejoa8sukbzyvn4rhqq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmejoa8sukbzyvn4rhqq7.png" alt="MCP capabilities image" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;
Image by &lt;a href="https://www.philschmid.de/mcp-introduction" rel="noopener noreferrer"&gt;philschmid&lt;/a&gt;



&lt;p&gt;MCP provides four capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; - Actions that the model can execute, such as sending an e-mail or querying a database;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resources&lt;/strong&gt; - Data that the model can access to retrieve context, similar to a GET request in a REST API (eg. access a file);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts&lt;/strong&gt; - Templates or workflows that guide the interaction between the model and the MCP server;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sampling&lt;/strong&gt; - Requests initiated by the MCP server for the model to perform an interaction, typically used as part of a tool's execution. Sampling is often overlooked, and until now few models support it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MCP Server Implementation using Quarkus
&lt;/h2&gt;

&lt;p&gt;Let’s build a simple MCP server that helps a car dealership sell cars. Since tools are the core of MCP, we’ll implement three tools in our server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tool to &lt;strong&gt;list&lt;/strong&gt; all available cars;&lt;/li&gt;
&lt;li&gt;A tool to &lt;strong&gt;fetch the price&lt;/strong&gt; of a specific car;&lt;/li&gt;
&lt;li&gt;A tool to &lt;strong&gt;purchase&lt;/strong&gt; a car.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is done using Java and Quarkus. The full implementation can be found in &lt;a href="https://github.com/diogodanielsoaresferreira/mcp-demo" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Singleton&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;McpResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Get the information about the available cars"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchAvailableCars&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"Mercedes-Benz C-Class"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mercedes-Benz E-Class"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mercedes-Benz S-Class"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mercedes-Benz GLC"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mercedes-Benz GLE"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Toyota Camry"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Toyota Corolla"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Toyota Land Cruiser"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Honda Civic"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Honda Accord"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Ford Focus"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Ford Mustang"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Chevrolet Cruze"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Chevrolet Malibu"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Nissan Altima"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Nissan Leaf"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Kia Optima"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Hyundai Elantra"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Hyundai Sonata"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Renault Clio"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Renault Megane"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Peugeot 208"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Peugeot 308"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Volvo S60"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Volvo V60"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mazda3"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Mazda CX-5"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Get information about a price of a car"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;fetchCarInfo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@ToolArg&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"brand"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mercedes-Benz C-Class"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"80000"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"100000"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Buy a car"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;buyCar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@ToolArg&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"userName"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@ToolArg&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"brand"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Successful transaction"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the server is implemented, let's test it locally with Claude.&lt;br&gt;
To do that, we need to configure the MCP server in the Claude MCP configuration as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"carDealershipTools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-jar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/Desktop/mcpdemo/target/mcpdemo-1.0-SNAPSHOT-runner.jar"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4vpt9x2dbmwuytk7kd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4vpt9x2dbmwuytk7kd2.png" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like the tools are correctly configured! Let's prompt Claude to buy a car and see if it works correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fax9es7zho05lv5s96f3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fax9es7zho05lv5s96f3d.png" width="800" height="1355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdz5mbi2kkl1gtm0vja0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdz5mbi2kkl1gtm0vja0.png" width="800" height="1248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8ux4qm12dhzu424ki38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8ux4qm12dhzu424ki38.png" width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There it is, our MCP server is providing the tools for a car dealership chatbot to aid customers in buying cars.&lt;/p&gt;

&lt;p&gt;There are already many useful &lt;strong&gt;MCP servers available&lt;/strong&gt;, ranging from weather services and file system tools to integrations with YouTube or Spotify. You can explore curated lists of these servers &lt;a href="https://mcpservers.org" rel="noopener noreferrer"&gt;here&lt;/a&gt; or &lt;a href="https://mcp.pipedream.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dangers of MCP Access
&lt;/h2&gt;

&lt;p&gt;MCP servers can act as an interface between AI agents and your APIs. But before rushing to build them, it’s important to understand the risks that come with granting agents access to powerful MCP tools.&lt;/p&gt;

&lt;p&gt;While MCP tools significantly enhance the capabilities of AI agents, they also introduce &lt;strong&gt;serious risks&lt;/strong&gt; if not properly managed. Granting an agent access to tools like file systems, payment processors, or external APIs means it can perform real-world actions with real-world consequences.&lt;br&gt;
Even if you control the model, what if someone performs a prompt injection attack to exploit a vulnerability in your tools?&lt;/p&gt;

&lt;p&gt;Without strict controls, validation, and sandboxing, &lt;strong&gt;a poorly instructed or malicious agent could delete data, leak sensitive information, or make unauthorized transactions&lt;/strong&gt;. The more powerful the tools, the higher the responsibility on developers to enforce safeguards, including permissioning, auditing, and limiting tool access to only what’s absolutely necessary.&lt;/p&gt;

&lt;p&gt;As many things, &lt;strong&gt;with great power comes great responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>quarkus</category>
      <category>java</category>
    </item>
    <item>
      <title>How to Build a Meaningful and Successful Career</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Thu, 26 Jun 2025 17:43:36 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/how-to-build-a-meaningful-and-successful-career-8jb</link>
      <guid>https://dev.to/diogodanielsoaresferreira/how-to-build-a-meaningful-and-successful-career-8jb</guid>
      <description>&lt;p&gt;Hi there! I have been working in Software Engineering for a few years now, and along the way, I've discovered some lessons I wish I knew earlier.&lt;br&gt;
These aren't technical skills or silver bullets, but they've helped me shape a long-term approach to my career.&lt;br&gt;
I'm writing this as a reminder to my future self, but I hope they can be useful to you too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have a clear long-term goal.
&lt;/h2&gt;

&lt;p&gt;Over the course of your career, it's important to know where do you want to go, instead of drifting without direction.&lt;br&gt;
Where do you want to be in 10 years?&lt;/p&gt;

&lt;p&gt;Do you want to work in a big tech company? Start your own business? Work abroad? Retire early?&lt;br&gt;
Do you want to switch roles or industries? Be a manager, or to work in a specific technology?&lt;/p&gt;

&lt;p&gt;Knowing the answer to this question is essential to know what you should do today to achieve it.&lt;br&gt;
Be as specific as you want. The more specific it is, the easier to visualize it and stay motivated along the way.&lt;/p&gt;

&lt;p&gt;If you know where you want to go, you can also better manage the risk - “Should I take this risky opportunity if I’m already close to my goal?” It’s also wise to have a plan B in case things don’t go as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmunlflq7y4ivt7l0gi9x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmunlflq7y4ivt7l0gi9x.jpg" alt="Have a goal" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@javaistan?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Afif Ramdhasuma&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/red-and-black-round-metal-jl4BQJs87Do?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;h2&gt;
  
  
  Most returns are compound. Play the long-term game.
&lt;/h2&gt;

&lt;p&gt;Careers can be very long. Decades long. Yet, most of us think of careers in a short time-frame: the next promotion, the next opportunity. However, as with most things in life, returns are compound. Not just in wealth, but in skills, experience and relationships.&lt;/p&gt;

&lt;p&gt;Sometimes it's better to sacrifice short-term gain for a long-term advantage - “Should I switch companies now for a higher salary, or stay to deepen my skills and grow into a better professional?”.&lt;br&gt;
If you already have a long-term goal in mind, the answer becomes clearer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtzw73fnf0t7yno4meo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtzw73fnf0t7yno4meo5.png" alt="The power of compound learning" width="593" height="408"&gt;&lt;/a&gt;&lt;/p&gt;
The power of compound learning by &lt;a href="https://dev.to/jurajmalenica/good-engineers-train-their-skills-great-engineers-train-their-mindset-21h"&gt;Juraj Malenica&lt;/a&gt;



&lt;h2&gt;
  
  
  Surrond yourself with people smarter than you.
&lt;/h2&gt;

&lt;p&gt;Your most valuable asset in your career is your knowledge. Keep learning consistently, and over time you’ll surpass those with more years of experience who stopped learning along the way.&lt;br&gt;
One of the best ways to learn is by working with people who are better than you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You are the average of the five people you spend the most time with.” - Jim Rohn&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We naturally absorb the behavior, work ethic, and mindset of those around us. That’s why your colleagues are one of the biggest influences on your career. Work with people you admire and respect. It should be a major factor in your decision to join (or leave) a company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn from your failures.
&lt;/h2&gt;

&lt;p&gt;Everyone faces failure. Some of these failures are small, and others can undo years of effort. But failure doesn't define a career, it shapes it.&lt;/p&gt;

&lt;p&gt;Each failure is an opportunity to learn something valuable. As painful as it may be, it contributes to your growth, if you’re willing to reflect and adapt.&lt;/p&gt;

&lt;p&gt;Recognize your failures, fail fast and learn from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Balance is key.
&lt;/h2&gt;

&lt;p&gt;Your career is not a sprint, it's a marathon. Life has many facets: career, health, relationships, finances, mental well-being. You're only as strong as your weakest area.&lt;br&gt;
If you focus too much on your career and neglect the rest, you risk burnout and dissatisfaction.&lt;/p&gt;

&lt;p&gt;Have an holistic approach. A sustainable, fulfilling career is built on consistency, not constant overwork.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6z9scu7ywgoxjsx9p3i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6z9scu7ywgoxjsx9p3i.jpg" alt="Balance is key" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@hautier?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Christophe Hautier&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/gray-top-902vnYeoWS4?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;h2&gt;
  
  
  Enjoy the ride!
&lt;/h2&gt;

&lt;p&gt;What you’ll truly remember years from now aren’t the titles you held or the size of your paycheck, but the good moments with your teammates. The late-night product launches, the mentors who believed in you, the times you struggled and grew, the laughs during coffe breaks.&lt;/p&gt;

&lt;p&gt;We often get so focused on getting ahead that we forget to look around. Celebrate the small wins. Reflect on how far you’ve come. Be present in your day-to-day. Make time to connect with others. These moments are what truly give meaning to your career.&lt;/p&gt;

&lt;p&gt;I hope you can take these tips and use them to improve your career!&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>career</category>
      <category>softwareengineering</category>
      <category>tips</category>
    </item>
    <item>
      <title>Beyond AI Code Generation: The Art of Mechanical Sympathy</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Wed, 05 Mar 2025 16:40:45 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/beyond-ai-code-generation-the-art-of-mechanical-sympathy-31bl</link>
      <guid>https://dev.to/diogodanielsoaresferreira/beyond-ai-code-generation-the-art-of-mechanical-sympathy-31bl</guid>
      <description>&lt;p&gt;Hi there! With the advent of AI tools capable of writing code at the level of a junior engineer, is the software developer job at risk? Should you still bother learning programming languages?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Power of Mechanical Sympathy
&lt;/h2&gt;

&lt;p&gt;Jackie Stewart, a former racing driver, once said "You don't have to be an engineer to be a racing driver, but you do have to have &lt;strong&gt;Mechanical Sympathy&lt;/strong&gt;". A driver doesn't need to know every detail of the engine, but understanding how components work together makes them better at their craft.&lt;/p&gt;

&lt;p&gt;The same applies to software engineering. This is also why &lt;strong&gt;learning about assembly language and CPU architectures and memory management&lt;/strong&gt; is in most computer science courses curriculum. Learning about those topics is crucial, even if you don’t write assembly daily. It allows you to write more efficient code and debug issues faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This knowledge is one of the key differences between a good and a great software engineer.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging and Performance
&lt;/h2&gt;

&lt;p&gt;Understanding what’s happening under the hood also helps when &lt;strong&gt;developing assumptions&lt;/strong&gt; about the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a request to your relational database is slow, should you use a NoSQL database? Or would a simple indexing change fix it?&lt;/li&gt;
&lt;li&gt;If your program crashes randomly, is it a race condition, a memory leak, or CPU cache contention?&lt;/li&gt;
&lt;li&gt;If your high-performance loop is slow, is it due to inefficient memory access patterns or branch mispredictions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By knowing how things work at a lower level, you can make &lt;strong&gt;educated assumptions&lt;/strong&gt; about performance issues and bugs instead of relying purely on trial and error.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Hands-On Example: Loop Optimization
&lt;/h2&gt;

&lt;p&gt;Let's take a look at an example. The loop below just sums all numbers in a list. When I run it in my PC for 100000000 numbers, it takes 0.339 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&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;The code below does the same thing. But when I run it under the same conditions is faster - 0.252 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&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="kt"&gt;int&lt;/span&gt; &lt;span class="n"&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&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;Why is the second version of code faster?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instruction-Level Parallelism (ILP)&lt;/strong&gt;: Modern CPUs can execute multiple independent instructions in parallel. The second version allows more additions to be processed simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop Control Overhead&lt;/strong&gt;: The first version has four times more loop condition checks (i &amp;lt; size), which add unnecessary CPU instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branch Mispredictions&lt;/strong&gt;: CPUs predict whether a loop will continue. If mispredicted, the pipeline must be flushed and restarted. The first loop has four times more branches, leading to more mispredictions and stalls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at another example. Consider summing all elements of a 2D matrix stored in memory. The code below takes 0.171 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define N 1000
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;sum&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;col&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="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;col&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;row&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="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;row&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="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&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;The code below does the same thing, but it takes only 0.129 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;sum&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;row&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="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;row&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;col&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="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;col&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="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&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;Why is the latter faster?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU Cache Efficiency&lt;/strong&gt;: Memory is fetched in contiguous blocks (cache lines). Since C stores 2D arrays row-by-row, row-major traversal (the second version) follows memory order, maximizing cache hits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Misses in Column-Major Order&lt;/strong&gt;: The first version jumps across rows, repeatedly causing expensive cache misses and slowing execution.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Engineers Still Matter
&lt;/h2&gt;

&lt;p&gt;As systems are getting more complex, distributed and scalable, &lt;strong&gt;AI lacks the ability to develop mechanical sympathy&lt;/strong&gt; for a system’s inner workings. Understanding how software interacts with hardware gives engineers an edge in writing high-performance code, debugging complex, low-level issues and making informed trade-offs in architecture decisions.&lt;/p&gt;

&lt;p&gt;And how can you improve the mechanical sympathy for your systems? A way to do that is to join an on-call rotation. Watching systems break in real time teaches you more about performance bottlenecks than any AI-generated explanation ever will.&lt;/p&gt;




&lt;p&gt;Understanding the layer beneath what you work on &lt;strong&gt;makes you a better engineer&lt;/strong&gt;. It gives you an intuition for how your code interacts with the hardware and helps you make better decisions, whether you're debugging a tricky performance issue or designing scalable systems.&lt;/p&gt;

&lt;p&gt;AI can assist in writing code, but &lt;strong&gt;true expertise comes from understanding the system as a whole&lt;/strong&gt;, which is something that AI struggles to do.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>c</category>
      <category>ai</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Using WebSockets to make an online pong game</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Sat, 08 Feb 2025 15:46:42 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/using-websockets-to-make-an-online-pong-game-783</link>
      <guid>https://dev.to/diogodanielsoaresferreira/using-websockets-to-make-an-online-pong-game-783</guid>
      <description>&lt;p&gt;Hi there! Full-duplex communication is becoming essential modern application. The days of relying solely on traditional request/response models with REST APIs are long gone. From multiplayer games to chat apps and collaborative editing, &lt;strong&gt;real-time updates are everywhere&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One of the most popular technologies for enabling real-time communication is WebSockets. In this post, I'll walk you through &lt;strong&gt;how I used WebSockets to implement a Pong game&lt;/strong&gt;. You can play the game and check out the code in &lt;a href="https://github.com/diogodanielsoaresferreira/pong" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Websockets?
&lt;/h2&gt;

&lt;p&gt;Before WebSockets became a standard in 2011, real-time communication was typically handled by &lt;strong&gt;polling&lt;/strong&gt;, where the client repeatedly sent HTTP requests to check for new data. However, this approach causes some problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High server load&lt;/strong&gt; - If 1000 clients poll the server every second, the server has to handle at least 1000 requests per second. As the number of client grows, the server can quickly become overwhelmed, processing a large number of redundant requests even when there is no new data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High overhead&lt;/strong&gt; - Every HTTP request starts a new connection, requiring a new TCP handshake and potentially a TLS handshake (for HTTPS). This adds significant overhead, mainly for frequent small requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased Latency&lt;/strong&gt; - Clients receive updates only at fixed intervals, meaning real-time events can be delayed, leading to a slower and less responsive experience, which is especially problematic in applications like gaming, chat and live collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of these reasons, HTTP polling often results in a laggy, inefficient and resource-intensive experience, making it a poor choice for real-time applications.&lt;/p&gt;

&lt;p&gt;There are other options to implement low-latency communication between a client and a server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt; are a simple and efficient way for the server to push updates to the client over a persistent connection. However, SSE are unidirectional, meaning the client cannot send messages back to the server over the same channel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC&lt;/strong&gt; is primarily designed for Peer-to-Peer (P2P) communication and excels in media streaming and real-time video/audio. However, for most applications, it’s overly complex, requiring significant setup, high CPU usage, and intricate network handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comet&lt;/strong&gt; is the predecessor to WebSockets, and enables the server to push updates to clients using long polling (keeping an HTTP connection open until new data is available). While it reduces latency, it wastes resources by keeping multiple HTTP connections open and still requires separate client requests for sending data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw Sockets&lt;/strong&gt; (TCP/UDP) provide true bidirectional, low-latency communication, but they don’t work well through firewalls and NATs since they don’t use standard HTTP ports. UDP is especially useful for high-performance applications like real-time multiplayer games, where minimizing latency is crucial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While each of these approaches has its place, WebSockets strike the right balance between efficiency, simplicity, and broad compatibility for most real-time applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  How do Websockets work?
&lt;/h2&gt;

&lt;p&gt;WebSockets are designed to work over the &lt;strong&gt;existing HTTP infrastructure&lt;/strong&gt;, ensuring compatibility with modern web architectures without requiring changes to network or client configurations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bx55q1ejz5o40s55gow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bx55q1ejz5o40s55gow.png" alt="Websocket handshake protocol" width="249" height="229"&gt;&lt;/a&gt;&lt;/p&gt;
By Brivadeneira - Own work, CC BY-SA 4.0, &lt;a href="https://commons.wikimedia.org/w/index.php?curid=82810859" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;



&lt;p&gt;The protocol is straightforward: the client initiates a handshake over HTTP, which is acknowledged by the server. From there, a persistent bidirectional channel is setup, and the client or the server can send messages at any time without waiting for a request. Either party can close the channel when needed.&lt;/p&gt;

&lt;p&gt;This solves the server overload problem because &lt;strong&gt;there is no polling&lt;/strong&gt;. The client no longer needs to repeatedly request updates, as the server can push data in real time.&lt;/p&gt;

&lt;p&gt;It also reduces the protocol overhead required by relying on HTTP handshake for the connection establishment while maintaining an efficient and lightweight communication channel.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implement an online Pong game
&lt;/h2&gt;

&lt;p&gt;My goal was to implement a 2-player online pong game using Python and the websockets library.&lt;/p&gt;

&lt;p&gt;The game follows a client-server model, where both players communicate with a central server via WebSockets to exchange game actions and state updates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte32b0mtfcd6eac0rh5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte32b0mtfcd6eac0rh5y.png" alt="Pong interaction diagram" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a game
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A player sends a &lt;code&gt;create&lt;/code&gt; message to the server, with a chosen game name.&lt;/li&gt;
&lt;li&gt;The server responds with a &lt;code&gt;created&lt;/code&gt; message. indicating that the game was created and it's waiting for an opponent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Joining a game
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The second player sends a &lt;code&gt;join&lt;/code&gt; message to the server with the name of the game that they want to join.&lt;/li&gt;
&lt;li&gt;The server answers with a &lt;code&gt;joined&lt;/code&gt; message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Game loop
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We now have both players connected, so the game is on! The server will broadcast for both players the state of the game: the ball position and paddle positions.&lt;/li&gt;
&lt;li&gt;Each player can send &lt;code&gt;move&lt;/code&gt; messages to control their paddles: &lt;code&gt;up&lt;/code&gt;, &lt;code&gt;down&lt;/code&gt;, or &lt;code&gt;none&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Preventing Cheating
&lt;/h3&gt;

&lt;p&gt;Each player can only send &lt;code&gt;up&lt;/code&gt; or &lt;code&gt;down&lt;/code&gt; messages, and cannot directly update their position (&lt;code&gt;move to position x&lt;/code&gt;). This is important to avoid players from cheating. Only the server should manage the game state and players should only send actions to the server, not state messages.&lt;/p&gt;




&lt;h2&gt;
  
  
  What about Network Latency?
&lt;/h2&gt;

&lt;p&gt;In this guide, I implemented a simple version of pong without focusing on network lag. However, in more complex, time-sensitive games, such as first-person shooters (FPS) or real-time strategy (RTS) games, &lt;strong&gt;high latency can degrade the game experience&lt;/strong&gt;, or even make the game unplayable. How to solve this problem?&lt;/p&gt;

&lt;p&gt;There are two common techniques that can be used to compensate for network latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-side Prediction
&lt;/h3&gt;

&lt;p&gt;In this case, the &lt;strong&gt;client predicts the result of the player's action&lt;/strong&gt; without waiting for the server's response. In most cases, the server should confirm the prediction and the player won't notice any difference. However, if the server sends a different state of the player, the client must correct the player's position, potentially causing a visual hitch.&lt;/p&gt;

&lt;p&gt;In the pong game, this would mean to move the paddle instantly when a player presses a key, without waiting for the server's confirmation. If the server later corrects the position, the paddle might appear to "jump" slightly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lag Compensation
&lt;/h3&gt;

&lt;p&gt;Imagine you're playing a first-person shooter and you clearly hit the enemy's head. But somehow you still miss. How is that possible? Maybe the action reached the server too late and the enemy was not there anymore.&lt;/p&gt;

&lt;p&gt;In this case, lag compensation should fix the problem. &lt;strong&gt;The client should not only send the action, but also the state of the world&lt;/strong&gt;. In that state of the world, the enemy would be shot. The server can then reconstruct the game state at that moment and process the action accordingly.&lt;/p&gt;

&lt;p&gt;While this makes shooting feel fairer for the attacker, it can feel frustrating for the target. For example, a player who already moved behind cover might still get hit because, in the past game state, they were still exposed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihpw1e8wxm2rbcsj7i6w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihpw1e8wxm2rbcsj7i6w.jpg" alt="Example of Lag Compensation" width="690" height="372"&gt;&lt;/a&gt;&lt;/p&gt;
Historic client enemy position (red) versus server enemy position (blue), &lt;a href="https://developer.valvesoftware.com/w/index.php?curid=1969" rel="noopener noreferrer"&gt;Valve Developer Community&lt;/a&gt;



&lt;p&gt;If you want to learn more about this topic, there is a great guide about it in &lt;a href="https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization" rel="noopener noreferrer"&gt;Valve developer community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For such a simple game such techniques would bring little benefit, so they were not applied.&lt;/p&gt;




&lt;p&gt;Now that you’ve seen how WebSockets enable real-time communication, why not experience it yourself? &lt;a href="https://github.com/diogodanielsoaresferreira/pong" rel="noopener noreferrer"&gt;&lt;strong&gt;Give the game a try and see how it feels!&lt;/strong&gt;&lt;/a&gt; You can play against an AI or even with two players on the same keyboard, a version that runs entirely locally without the need for WebSocket communication.&lt;/p&gt;

&lt;p&gt;Feel free to &lt;a href="https://github.com/diogodanielsoaresferreira/pong" rel="noopener noreferrer"&gt;clone the repository&lt;/a&gt;, play the game, and improve it with your own ideas.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>backenddevelopment</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Run your program in the kernel space with eBPF</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Sat, 13 Jul 2024 12:55:21 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/run-your-program-in-the-kernel-space-with-ebpf-438l</link>
      <guid>https://dev.to/diogodanielsoaresferreira/run-your-program-in-the-kernel-space-with-ebpf-438l</guid>
      <description>&lt;p&gt;Hi there! Have you heard about eBPF? eBPF is not a new technology, but its usage has been growing in some areas, such as network security, network observability and performance monitoring. However, the implications of allowing users to run user code in kernel space can have a much wider impact than just in those areas. Let's find out how it works!&lt;/p&gt;




&lt;p&gt;To understand eBPF, we must first understand what is the kernel space and the user space in an operative system. The &lt;strong&gt;user space&lt;/strong&gt; is where most programs run. The &lt;strong&gt;kernel space&lt;/strong&gt; is where the OS runs. The kernel space has privileged access to hardware, such as devices, file access and networks. That's why device drivers usually run on kernel space. However, if a program on the kernel breaks, it can break the whole OS, so most programs run in user space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8a38naa92lffgzh7tj9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8a38naa92lffgzh7tj9q.png" alt="The user space and the kernel space." width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;The user space and the kernel space. Source: &lt;a href="https://eunomia.dev/tutorials/0-introduce/" rel="noopener noreferrer"&gt;Eunomia&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;When a program runs on the user space, it can interact with the kernel space through an API - the &lt;strong&gt;system calls&lt;/strong&gt;. If a program wants to write to a file, it does not need to access the underlying memory directly; in a similar case, if a program wants to send packets over the network, it does not access the network controller directly; instead, it relies on the system calls, that expose an abstraction of the resources.&lt;/p&gt;

&lt;p&gt;While the system calls allow for the OS to expose an abstraction of the resources, they also limit what is possible to do with them. A program interaction with the hardware is limited by the operations available through the system calls. Sometimes the system calls can even be slower than accessing the hardware directly. Each system call has a performance penalty of crossing the user/kernel space, and if an application in the user space requires access to the kernel space often, its performance can be heavily penalized.&lt;/p&gt;

&lt;p&gt;For those reasons, it may make sense to run a program in the kernel space. You have mainly two options to do that: firstly, you can request to add your code to the Linux kernel. This option can take years to be accepted and released to the general public, but makes sense if your program is useful to the kernel of the OS. Secondly, you can use eBPF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9pzti19ua6mmmahtz1d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9pzti19ua6mmmahtz1d.jpg" alt="Comic about eBPF and Linux kernel" width="800" height="343"&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://isovalent.com/blog/post/ebpf-documentary-creation-story/" rel="noopener noreferrer"&gt;eBPF Comic by Philipp Meier and Thomas Graf&lt;/a&gt;
&lt;/p&gt;



&lt;p&gt;BPF (&lt;strong&gt;Berkeley Packet Filter&lt;/strong&gt;) is a kernel program that filters packets before they are read by the kernel. This is done with a small virtual machine that can be programmed by applications in the user space. Using BPF, you can perform packet filtering in an extremely efficient way, with the tradeoff of being limited by a small instruction set defined by BPF.&lt;/p&gt;

&lt;p&gt;eBPF (&lt;strong&gt;extended BPF&lt;/strong&gt;) is mainly an extension of the instruction set of the original BPF, with custom data structures (maps), helper functions, and tail calls, among others. With the new extension set, eBPF is not limited to networking use cases such as packet filtering, but can allow to trace and manipulate kernel function calls, syscalls, and other system events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0660w8bmj659d61ab1y0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0660w8bmj659d61ab1y0.png" alt="Interaction between eBPF and the Linux kernel" width="800" height="341"&gt;&lt;/a&gt;&lt;br&gt;Depiction of the interaction between eBPF and the Linux kernel. Source: &lt;a href="https://oswalt.dev/2021/01/introduction-to-ebpf/" rel="noopener noreferrer"&gt;Introduction to eBPF&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;In this way, you can code your program from the user space to be run in the kernel space, at runtime, and sandboxed.&lt;/p&gt;

&lt;p&gt;As code is being run in the kernel space, additional measures must be taken to ensure that the code does not introduce problems in the kernel. eBPF performs security checks on the bytecode before loading it into memory, ensuring some problems do not happen, such as memory out-of-bounds. Many features in the virtual machine that can pose security risks or that may expand the attack surface are also disabled by default.&lt;/p&gt;

&lt;p&gt;With these innovative features, eBPF is now used widely in data centers of large companies, such as Facebook, Google or Netflix, with a focus on network monitoring, performance monitoring, or network observability.&lt;/p&gt;



&lt;p&gt;Let's implement a simple program using eBPF. The program will monitor the deleted files in the system and print their filename.&lt;/p&gt;

&lt;p&gt;To do that, we will use the &lt;a href="https://eunomia.dev/" rel="noopener noreferrer"&gt;eunomia-bpf tool&lt;/a&gt; to help us with the development. The eBPF program is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"vmlinux.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bpf/bpf_helpers.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bpf/bpf_tracing.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bpf/bpf_core_read.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;LICENSE&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;SEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Dual BSD/GPL"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;SEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kprobe/do_unlinkat"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;BPF_KPROBE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_unlinkat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;dfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pid_t&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_get_current_pid_tgid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BPF_CORE_READ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;bpf_printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"KPROBE ENTRY pid = %d, filename = %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;This code is from the &lt;a href="https://eunomia.dev/tutorials/2-kprobe-unlink/" rel="noopener noreferrer"&gt;eunomia tutorial&lt;/a&gt;. As you can see, the eBPF code is very similar to C. In reality, eBPF programs are written in a restricted subset of C.&lt;/p&gt;

&lt;p&gt;eBPF allows the user to write programs (probes) to execute before or after a system call is handled. In this case, we are defining a probe (&lt;code&gt;kprobe/do_unlinkat&lt;/code&gt;) that executes before the system call &lt;code&gt;do_unlinkat&lt;/code&gt; is executed. This system call is called when a file is deleted. Almost all functions in the kernel can be probed, including system calls or interrupt handlers.&lt;/p&gt;

&lt;p&gt;The function takes as parameters the file descriptor and a pointer to the name of the file being removed. In the code, we retrieve the PID of the process executing the system call and the filename, and print them in the kernel log.&lt;/p&gt;

&lt;p&gt;The kernel log can be accessed in the file &lt;code&gt;/sys/kernel/debug/tracing/trace_pipe&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Let's compile and run the program using the ecc tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./ecc kprobe-link.bpf.c
INFO &lt;span class="o"&gt;[&lt;/span&gt;ecc_rs::bpf_compiler] Compiling bpf object...
INFO &lt;span class="o"&gt;[&lt;/span&gt;ecc_rs::bpf_compiler] Generating package json..
INFO &lt;span class="o"&gt;[&lt;/span&gt;ecc_rs::bpf_compiler] Packing ebpf object and config into package.json...
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./ecli run package.json
INFO &lt;span class="o"&gt;[&lt;/span&gt;faerie::elf] strtab: 0x4f9 symtab 0x538 relocs 0x580 sh_offset 0x580
INFO &lt;span class="o"&gt;[&lt;/span&gt;bpf_loader_lib::skeleton::poller] Running ebpf program...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the program is running, let's create and delete a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;toBeDeleted
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;toBeDeleted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's see what is in the output file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;sudo cat&lt;/span&gt; /sys/kernel/debug/tracing/trace_pipe

           &amp;lt;...&amp;gt;-7727    &lt;span class="o"&gt;[&lt;/span&gt;001] ....1   867.906922: bpf_trace_printk: KPROBE ENTRY pid &lt;span class="o"&gt;=&lt;/span&gt; 7727, filename &lt;span class="o"&gt;=&lt;/span&gt; toBeDeleted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There may be many log lines, but you should find the indication of the file you just deleted.&lt;/p&gt;

&lt;p&gt;If you're curious, play other applications and see which files they remove. You may be surprised!&lt;/p&gt;

&lt;p&gt;Probes are just a single feature of many in eBPF, so if you want to explore more, follow &lt;a href="https://eunomia.dev/tutorials/" rel="noopener noreferrer"&gt;other tutorials from Eunomia&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;As you can see, &lt;strong&gt;probing a system call from the user space would not be possible without eBPF&lt;/strong&gt;. eBPF opens up a lot of possibilities for exploring the kernel space like it was not possible before. For programs that must interact with the low-level details of the operative system, such as file access, networking processes, hardware access, or other kernel operation, eBPF is a great tool to have under your belt.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>c</category>
      <category>programming</category>
      <category>linux</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How I plan to (be able to) retire at 35</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Fri, 14 Jun 2024 14:08:42 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/how-i-plan-to-be-able-to-retire-at-35-1mp6</link>
      <guid>https://dev.to/diogodanielsoaresferreira/how-i-plan-to-be-able-to-retire-at-35-1mp6</guid>
      <description>&lt;p&gt;Hi there! Some years ago, I was listening to an old interview with Agostinho da Silva, a Portuguese philosopher who died 30 years ago, and he said something that has been in my mind ever since: "The Man is not born to work, the Man is born to create".&lt;/p&gt;

&lt;p&gt;This goes contrary to the current belief in developed countries, which says that the life of a Man must be dedicated to his work for most of his years.&lt;/p&gt;

&lt;p&gt;Who wouldn't like to spend more time on himself and his hobbies instead of working? However, in the current days, with the age of retirement increasing and the pension system more and more fragile, it seems that one is doomed to spend his time working to maintain his quality of life.&lt;/p&gt;

&lt;p&gt;In this post, I'll explain how I plan to be able to retire at 35, and how to have a plan for your financial life.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why should I be worried about my retirement?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhup05otdk4hbazufbf9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhup05otdk4hbazufbf9.gif" alt="Relaxed guy" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
Source: &lt;a href="https://giphy.com/gifs/relax-florida-hammock-7Je6xJ0kPxUORZSvv8" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt;



&lt;p&gt;With the increase in the retirement age and a growing elderly population in most developed countries, the pension system of most developed countries is under big pressure. With the planned decrease of the real value of pensions in most countries, it seems certain that if someone wants to keep their quality of life after retirement, they must save money by their means. Besides, a retirement plan also allows you to plan an early retirement, or at least to reach financial independence.&lt;/p&gt;

&lt;p&gt;An early retirement means you can retire before retirement age and live off the return of your investments, doing minimal work. It means to focus on what you want, without being tied to a job. It means to do whatever you want without money holding you back. Do you want to spend a year traveling? You can do it. Do you want to spend a year doing a course? Do you want to learn an instrument? Or spend more time with your family? You can have time to do all that once you are financially independent.&lt;/p&gt;

&lt;p&gt;If you like your job and want to keep working, that's also not a problem. Just because you've reached financial independence, it does not mean that you must stop working.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setting your goal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbormdxs3ysb0ujmtx746.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbormdxs3ysb0ujmtx746.jpg" alt="Darts image" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://flic.kr/p/f71k" rel="noopener noreferrer"&gt;Martin Cathrae&lt;/a&gt; on Flickr



&lt;p&gt;To reach financial independence, the first step is to define your goal. It is different for everyone, and everyone has a different number. But a rule of thumb is that you can retire when you have invested 25x the value of your annual expenses. This value assumes that your investments return on average 4% by year, a rather conservative return rate. For example, if you spend 10.000€ each year, you can retire when you have invested 250.000€ and live off of the investment returns.&lt;/p&gt;

&lt;p&gt;The 4% return is based on some assumptions. For example, you'll live approximately 30 years past your retirement date, you have a portfolio of 50% stock and 50% bonds, and you are not including taxes or investment fees. For a more in-depth discussion about it, check out this &lt;a href="https://corporate.vanguard.com/content/dam/corp/research/pdf/Fuel-for-the-F.I.R.E.-Updating-the-4-rule-for-early-retirees-US-ISGFIRE_062021_Online.pdf" rel="noopener noreferrer"&gt;Vanguard analysis&lt;/a&gt; or this &lt;a href="https://thepoorswiss.com/updated-trinity-study/" rel="noopener noreferrer"&gt;updated study&lt;/a&gt;. We will stick to 4% for the rest of the discussion, however it may vary slightly depending on your investment profile.&lt;/p&gt;

&lt;p&gt;After defining your goal, you must define the steps to achieve it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where to invest
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro8ttqf5k2kmjrk2dxyx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro8ttqf5k2kmjrk2dxyx.gif" alt="Relaxed old guy" width="500" height="282"&gt;&lt;/a&gt;&lt;/p&gt;
Source: &lt;a href="https://giphy.com/gifs/showtime-ray-donovan-jon-voight-mickey-xTiTniXrZvZBrDIZri" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt;



&lt;p&gt;You want your investment to have some characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It must have an equal to or higher return than the market. You don't want to invest most of your money in a savings account or on most of the capital-guaranteed products.&lt;/li&gt;
&lt;li&gt;You want your investment to have low fees, to avoid the return of your investment being mostly used to cover the investment fees.&lt;/li&gt;
&lt;li&gt;You want your investment to be diversified so that you don't have to worry about losing all your money because a single company or market is down; in this way, you are reducing volatility.&lt;/li&gt;
&lt;li&gt;You want your investments to be simple to understand so you can do it easily and consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the FIRE (Financial Independence, Retire Early) community, one of the most popular strategies for investing in the long-term is Passive ETFs. ETFs (Exchange-Traded Funds) are funds structured to track a group of securities - a group of stocks, for example. When an ETF tracks different markets, with many companies of different industries, diversification is achieved. Being passive means that no portfolio manager makes the decisions on when to buy and when to sell. It usually means lower fees than active funds.&lt;/p&gt;

&lt;p&gt;In some studies, it's also shown that most simple investment strategies yield better results than complex ones. In 2007, Warren Buffet bet a million dollars that over a decade, a simple S&amp;amp;P500 Index Fund (a fund that tracks the biggest 500 companies in the USA market) would outperform a basket of hand-picked active managed funds. Ten years later, the S&amp;amp;P500 index would gain 125.8%, while the other five chosen funds had gains of 21.7%, 42.3%, 87.7%, 2.8% and 27.0%.&lt;/p&gt;

&lt;p&gt;A passive strategy usually has better results because of the lower fees. Besides, most funds, even though they are managed by highly skilled analysts, do not beat the market. If they do not beat the market, the odds are that you will also not beat the market. So instead of choosing individual stocks to beat the market, FIRE long-term investors focus on investing in the market, which is what ETFs track, at low cost.&lt;/p&gt;

&lt;p&gt;Some of the most popular ETFs are ETFs that track the S&amp;amp;P500 index (eg. SPY, VUAA, SXR8), ETFs that track the world economy (eg. VWCE, IUSQ), ETFs that track developed markets (eg. IWDA, SWRD, VGVF), or ETFs that track emerging markets (EMIM, IEMA, VFEA).&lt;/p&gt;


&lt;h2&gt;
  
  
  Focus on the savings rate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dwuk23p5zrtlknyvjhe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dwuk23p5zrtlknyvjhe.gif" alt="Man with lots of math equations around him" width="300" height="300"&gt;&lt;/a&gt;&lt;/p&gt;
Source: &lt;a href="https://giphy.com/gifs/rodneydangerfield-thinking-math-rodney-gEvab1ilmJjA82FaSV" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt;



&lt;p&gt;After knowing where to invest, it's time to optimize your life for your investments. The savings rate is the percentage of money you can save every month from your income. It's probably the most important number to focus on. You want it to be as high as possible so that you can invest as much as possible from your income.&lt;/p&gt;

&lt;p&gt;As a rule of thumb, you can know when you will reach your financial independence number by looking at your savings rate:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x=50∗(1−savingsrate)1.5x = 50 * (1 - savings rate)^{1.5}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;50&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="mord mathnormal"&gt;a&lt;/span&gt;&lt;span class="mord mathnormal"&gt;v&lt;/span&gt;&lt;span class="mord mathnormal"&gt;in&lt;/span&gt;&lt;span class="mord mathnormal"&gt;g&lt;/span&gt;&lt;span class="mord mathnormal"&gt;sr&lt;/span&gt;&lt;span class="mord mathnormal"&gt;a&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mord mathnormal"&gt;e&lt;/span&gt;&lt;span class="mclose"&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;1.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;where x is the number of years that will take you to reach your financial independence number (assuming it's 4% of your annual expenses).&lt;/p&gt;

&lt;p&gt;If you have a savings rate of 40%, it will take 23 years to reach financial independence. However, if your savings rate is 60%, it will take just 12 years. It's important to invest as much and as early as possible because, as investment interest compounds, it can make a big difference in a few years.&lt;/p&gt;

&lt;p&gt;To increase the savings rate, there are two things you can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increase income&lt;/strong&gt; - this does not come as a surprise, but increasing income is probably the best way to increase the savings rate. Unlike reducing expenses, there is no upper limit to how much we can increase our income. Reevaluate your job situation and the market. Can you increase your income by switching jobs? Can you ask for a raise from your boss?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce expenses&lt;/strong&gt; - reducing expenses is not about depriving yourself and living a depressing life, but instead focusing on what is important and reducing the superfluous waste. You want to make conscious choices to live efficiently, knowing that you are investing for a better future. This often includes renegotiation of energy/water/gas contracts, canceling unnecessary subscriptions, and avoiding unnecessary spending.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Changing your mindset
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o04u6vklrbctosx6m5r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o04u6vklrbctosx6m5r.gif" alt="Minimalism Gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
Source: &lt;a href="https://giphy.com/gifs/theminimalists-minimalism-simplicity-the-minimalists-rFDMmUYI9P3GCVtalR" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt;



&lt;p&gt;When reducing expenses, we are confronted with some crossroads and difficult decisions. Should we sell our car and use public transportation? Should we reduce our vacation spending and focus on cheaper alternatives? Should we move to a smaller house to save on the rent? Those are very personal decisions and there is no one-size-fits-all solution. What you need to understand is that you define your priorities. You can calculate how much you will save by selling your car or moving to a smaller house, and then calculate how much time that decision will save you in reaching financial independence.&lt;/p&gt;

&lt;p&gt;The key to reducing your expenses consistently is to find a balance between your present and future well-being. Discover your priorities and use the money accordingly. Such a mindset is called &lt;strong&gt;valuist - someone who discovers what they intrinsically value, and creates more of that&lt;/strong&gt;. For a valuist, the money to upgrade your car or move into a larger house, can be invested and grow over time, and it can have much more value a few years from now. A valuist will only spend that money if he believes that the use of the money now will be better than in a few years (that can be multiplied by 2 or 3).&lt;/p&gt;

&lt;p&gt;As someone who focuses on experiences, a valuist mindset is similar to a minimalist or a frugalist. A &lt;strong&gt;minimalist mindset focuses on finding freedom by owning only what adds value to someone's life&lt;/strong&gt;, and doing that, focusing on experiences and making more deliberate decisions. A &lt;strong&gt;frugalist mindset focuses on paying less for what you need&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As you can see, these three mindsets complement each other and make it easier to live a happy and fulfilling life while keeping your expenses low and helping you reach financial independence.&lt;/p&gt;


&lt;h2&gt;
  
  
  There are still many misconceptions about the idea of retiring early
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59wla24b5t51fnioz8cb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59wla24b5t51fnioz8cb.gif" alt="Man with many questions" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;
Source: &lt;a href="https://giphy.com/gifs/netflix-spike-lee-shes-gotta-have-it-sghi-ZaJtnTY8tFZz0PvmW9" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;"I don't invest because I don't have time to always be looking for the stock prices"&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are investing in the long-term, probably you shouldn't be always watching the stock prices, because you are not focused on short-term results. When you invest in the long-term, the returns can take some time.&lt;/p&gt;

&lt;p&gt;In 2014, a study was conducted by Fidelity on its client accounts. The study tried to identify the best investors by reviewing account returns. They discovered that the best investors were already dead or had forgotten to log on to their accounts for a long time. This only shows that most of the time, the best you can do to your investment is to do nothing.&lt;/p&gt;

&lt;p&gt;Reevaluate your investments every 3 or 6 months, but remind yourself that the biggest mistake every investor makes is to sell the investments too early.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;"I need to learn a lot before I start investing"&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you follow a simple investment plan, like I suggested above, the knowledge you need to start investing is minimal. There is no need to know how to analyze a balance sheet or an annual report. Many people overrate the need to learn everything about investing, but like many other things, investing is mostly learned on the go. If you wait to learn everything before investing, chances are you will never do it. If you feel you lack some knowledge, you can spend some time learning, but do it within a defined time-frame, 3 months or so. When that time-frame has passed, you know it's time to start investing. Time is a crucial part of investing, and studies show that you should start as early as possible, so don't waste time (and money) and start as soon as possible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;"I am going to lose all my money if I start investing"&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of people are afraid of losing all their money if they start investing. That's understandable, given the stories they heard from their parents and friends. However, investing does not need to be so risky. In reality, when following a diversified strategy as suggested above and focusing on the long-term, history suggests that it's uncommon to lose money at all when the years go by. Even more, the probability of higher returns only increases with time. So time is your best ally.&lt;/p&gt;

&lt;p&gt;Moreover, most passive ETFs are capitalization-weighted, which means that the weight of every company is determined by its market value. If a company goes bankrupt or its stock crashes, its impact on the ETF is reduced because its weight in the ETF decreases over time. On the other hand, if another company is growing, its weight in the ETF will increase over time. By investing in a capitalization-weighted ETF, you are protecting yourself from the impact that a single company can have on your portfolio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;"If I stop working, I don't know what to do and I will get depressed"&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a real problem for many of the people that retire early. From an early age, we were trained to live to work. This mindset is deeply in our minds, so much so that we connect our identity with our work - what we do is what we are, it's our purpose. When you retire, you lose your purpose, which can have a very negative impact on your identity and your mental health. Many early retirees feel anxious or depressed months after their retirement date. Some studies even relate an early retirement to a decline in health.&lt;/p&gt;

&lt;p&gt;While stopping work can be a big change for someone who from their early years always had their days occupied, there are some techniques that you can try to get used to this new chapter of life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before you fully commit to early retirement, try to imagine how your life will be. How will you fill your days? What will be your day-to-day routine?&lt;/li&gt;
&lt;li&gt;Have some hobbies that you like to spend time doing. Maybe you like to practice sports, gardening, or volunteering. This can be a great way to keep yourself active and happy with your life while having a healthier lifestyle.&lt;/li&gt;
&lt;li&gt;Create a bucket list of things to do. What did you always want to do that you never have the time? Learn a new language? Learn a new musical instrument? Travel the world? Now it's the time to do it!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, life after early retirement can be a challenge, at least in the beginning. It's an adaptation to a whole new life pace, where you get to call all the shots on your next step. However, if planned correctly, it can also be a second life where you have the time and the knowledge to be who you want to be, without any financial burden holding you back.&lt;/p&gt;




&lt;h2&gt;
  
  
  Will I actually retire early?
&lt;/h2&gt;

&lt;p&gt;I'm planning so that I can reach financial independence as soon as possible. I'm currently 25% through it, and by my estimates, I plan to reach it when I'm 35.&lt;/p&gt;

&lt;p&gt;I don't know yet if I will want to retire early. However, I would like to have the option. Reaching financial independence is a superpower that allows you to have bigger freedom, to live on your own terms, and to have an extra piece of mind.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER: I am not a financial advisor. The content of this post is for educational purposes only and merely cites my personal opinions. To make the best financial decision that suits your own needs, you must conduct your own research. Know that all investments involve some form of risk and there is no guarantee that you will be successful in making, saving, or investing money; nor is there any guarantee that you won't experience any loss when investing.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>investing</category>
      <category>financialindependence</category>
      <category>fire</category>
      <category>retirement</category>
    </item>
    <item>
      <title>Using contract testing for your microsservices</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Tue, 14 May 2024 17:17:34 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/using-contract-testing-for-your-microsservices-323m</link>
      <guid>https://dev.to/diogodanielsoaresferreira/using-contract-testing-for-your-microsservices-323m</guid>
      <description>&lt;p&gt;Hi there! Contract testing is a great tool for building microservices architectures, enabling the fast development of interacting components while maintaining confidence in the integrity of the system as a whole. By defining and verifying contracts between services, teams can iterate rapidly without the fear of inadvertently breaking downstream dependencies. In this blog post, we'll explore the fundamentals of contract testing, its benefits, and practical tips for implementation. Let's get started!&lt;/p&gt;




&lt;p&gt;Contract testing is a way to define and test an &lt;strong&gt;interface between two services that need to communicate&lt;/strong&gt;. It can be between a web server and a front-end client, but it can also be between a consumer and a provider of a message broker.&lt;/p&gt;

&lt;p&gt;Many times, two teams agree on an interface for two services to communicate, only to discover in integration testing (or worse, in production) that there was some misconception about how the services should interact. This often results in frustrating delays, costly rework, and a lot of finger-pointing.&lt;/p&gt;

&lt;p&gt;Contract testing aims to address this issue head-on by providing a mechanism for teams to validate their assumptions about service contracts early and continuously throughout the development lifecycle. By detecting discrepancies in expectations upfront, teams can preemptively resolve issues and ensure smoother integrations down the line.&lt;/p&gt;

&lt;p&gt;The goal of contract testing is not to replace integration testing. However, it's to reduce the amount of integration testing needed, by testing the contract earlier in the development cycle, allowing for faster feedback loops to speed up software delivery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqy91n8ilfsiqb9crgux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqy91n8ilfsiqb9crgux.png" alt="Consumer-Driven Contract Tests for Microservices pyramid" width="685" height="527"&gt;&lt;/a&gt;&lt;br&gt;Source: Consumer-Driven Contract Tests for Microservices: A Case Study, Lehvä, J., Mäkitalo, N., Mikkonen, T. (2019)
&lt;/p&gt;



&lt;p&gt;Let's look at an example to understand how it works.&lt;/p&gt;

&lt;p&gt;Every contract needs at least one consumer and one producer. We will implement in Python (using Flask) a simple client-server interaction. The server has an endpoint for requesting the stock ticker of a company. For example, when performing a GET request to &lt;code&gt;/ticker/Amazon&lt;/code&gt;, it should answer with &lt;code&gt;AMZN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amazon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Microsoft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSFT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/ticker/&amp;lt;company&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_company_ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client has the goal of discovering in which stock exchange the company is listed. Amazon, for example, is listed in NASDAQ stock exchange.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StockExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_uri&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_uri&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_company_stock_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;stock_exchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NASDAQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSFT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NASDAQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NASDAQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_uri&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/ticker/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stock_exchange&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stock_exchange&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the StockExchange class requests the ticker of a company to the previous service. How can we test this interaction? We have three options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5uai7y6oidv4suu6jwd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5uai7y6oidv4suu6jwd.gif" alt="Integration testing flow depiction" width="570" height="410"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://pactflow.io/how-pact-works/?utm_source=ossdocs&amp;amp;utm_campaign=getting_started#slide-1" rel="noopener noreferrer"&gt;PactFlow&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;We start an instance of the server everytime we want to test the client. This scenario would be the closest to the production environment. However, it would also be the most costly option in terms of time and resources. Setting up and tearing down the server for each test can slow down the testing process and make it less efficient, especially as the application grows in complexity. We should rely on integration tests as little as possible.&lt;/p&gt;
&lt;h2&gt;
  
  
  Unit testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstsxnv94yx58weeqs21u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstsxnv94yx58weeqs21u.gif" alt="Unit testing flow depiction" width="570" height="410"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://pactflow.io/how-pact-works/?utm_source=ossdocs&amp;amp;utm_campaign=getting_started#slide-1" rel="noopener noreferrer"&gt;PactFlow&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;With unit tesing, we can simulate the behaviour of the server without actually starting it. This means that we do not need to start in instance of it everytime we want to run a test in the client. However, it's also more error prone, since the expected implementation of the server can actually diverge from the actual implementation. This means that we may actually be setting up our mock with wrong data and having wrong expectations in tests, and we may never find until the error comes up in integration testing or in a live environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contract testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc6xniro93axx915l86r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc6xniro93axx915l86r.gif" alt="Contract testing flow depiction part 1" width="570" height="410"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://pactflow.io/how-pact-works/?utm_source=ossdocs&amp;amp;utm_campaign=getting_started#slide-1" rel="noopener noreferrer"&gt;PactFlow&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Contract testing solves the problem of diverging the expectations from the actual implementation without the need to start the server. From the client (or consumer) side, setting up a contract test is very similar to setting up a mock. However, after the test is run, a contract is generated, that can then be tested against the actual server. If the implementation diverges from what is specified in the contract, the test will fail. In this way, we can also test the integration between both parties without the need for a dedicated test environment and removing the need for release coordination, because we have static knowledge about system compatibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flehtay0o05kbxfubh3tk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flehtay0o05kbxfubh3tk.gif" alt="Contract testing flow depiction part 2" width="570" height="410"&gt;&lt;/a&gt;&lt;br&gt;Source: &lt;a href="https://pactflow.io/how-pact-works/?utm_source=ossdocs&amp;amp;utm_campaign=getting_started#slide-1" rel="noopener noreferrer"&gt;PactFlow&lt;/a&gt;
&lt;/p&gt;



&lt;p&gt;To implement contract tests we will be using pytest and Pact, the most popular tool for designing contract tests.&lt;br&gt;
Let's start by implementing the tests on the client side. First, let's set up pact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StockExchange&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pact&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;

&lt;span class="n"&gt;PACT_MOCK_HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;PACT_MOCK_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;
&lt;span class="n"&gt;PACT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StockExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://{host}:{port}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PACT_MOCK_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PACT_MOCK_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;StockExchange&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;has_pact_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TickerService&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;host_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PACT_MOCK_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PACT_MOCK_PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pact_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PACT_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;pact&lt;/span&gt;
    &lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop_service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we only need to implement the tests just as we would do with a mock.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_ASML_stock_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pact&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Stock exchange&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upon_receiving&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;company ticker ASML&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/ticker/ASML&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;will_respond_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_company_stock_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_AMZN_stock_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pact&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Stock exchange&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upon_receiving&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;company ticker Amazon&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/ticker/Amazon&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;will_respond_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_company_stock_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amazon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NASDAQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it's done! We can run the tests with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pytest
...
======================== 2 passed, 16 warnings in 2.15s ========================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the tests have run, pact generates a contract that states what the client expects from the server. This is called "provider contract".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"consumer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StockExchange"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TickerService"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"company ticker ASML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"providerState"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stock exchange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/ticker/ASML"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ASML"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"company ticker Amazon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"providerState"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stock exchange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/ticker/Amazon"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AMZN"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pactSpecification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, given a get request &lt;code&gt;/ticker/ASML&lt;/code&gt;, the server should answer with &lt;code&gt;ASML&lt;/code&gt;, and given a get request &lt;code&gt;/ticker/Amazon&lt;/code&gt;, the server should answer with &lt;code&gt;AMZN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the contract, we can now run the server and test if the expectations apply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python server.py
...
&lt;span class="nv"&gt;$ &lt;/span&gt;pact-verifier &lt;span class="nt"&gt;--provider-base-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:5000 &lt;span class="nt"&gt;--pact-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stockexchange-tickerservice.json
...
2 interactions, 0 failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's introduce an error in the server and run again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ASML&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amazon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Microsoft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSFT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python server.py
...
&lt;span class="nv"&gt;$ &lt;/span&gt;pact-verifier &lt;span class="nt"&gt;--provider-base-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:5000 &lt;span class="nt"&gt;--pact-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stockexchange-tickerservice.json
...
       Matching keys and values are not shown

       -&lt;span class="s2"&gt;"AMZN"&lt;/span&gt;
       +&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;


       Description of differences
       &lt;span class="nt"&gt;--------------------------------------&lt;/span&gt;
       &lt;span class="k"&gt;*&lt;/span&gt; Expected &lt;span class="s2"&gt;"AMZN"&lt;/span&gt; but got &lt;span class="s2"&gt;"ERROR"&lt;/span&gt; at &lt;span class="err"&gt;$&lt;/span&gt;

2 interactions, 1 failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, using only mocks we would not have catched the error.&lt;/p&gt;

&lt;p&gt;A common mistake of many developers when making contract testing is to test the contract itself. In this case, it would be to test that the server returns &lt;code&gt;ASML&lt;/code&gt; or &lt;code&gt;AMZN&lt;/code&gt;. However, the goal of contract testing is to be able to test the client funcionality and not the contract itself. When using contract testing, &lt;strong&gt;be sure to not test the mock and test the funcionality instead&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pact is not the only implementation available, but it's the most widely used. It's also possible to integrate your OpenAPI schema with Pact and have more extensive testing. It's also possible to use a Pact Broker to share contracts across clients and producers, which can be very useful in a large organization.&lt;/p&gt;

&lt;p&gt;If you want to find out more, check out this &lt;a href="https://martinfowler.com/bliki/ContractTest.html" rel="noopener noreferrer"&gt;great article on Contract Testing by Martin Fowler&lt;/a&gt;, or check &lt;a href="https://docs.pact.io/faq/convinceme" rel="noopener noreferrer"&gt;this Pact page&lt;/a&gt; on why to use contract testing.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>backenddevelopment</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Should I use Virtual Threads in Java?</title>
      <dc:creator>Diogo Daniel Soares Ferreira</dc:creator>
      <pubDate>Fri, 12 Apr 2024 07:49:45 +0000</pubDate>
      <link>https://dev.to/diogodanielsoaresferreira/should-i-use-virtual-threads-in-java-3986</link>
      <guid>https://dev.to/diogodanielsoaresferreira/should-i-use-virtual-threads-in-java-3986</guid>
      <description>&lt;p&gt;Hi there! A lot has been talked about in the last two years of Virtual Threads and how they can revolutionize the Java concurrent programming model and, consequently, increase dramatically the throughput of web applications. But should you use virtual threads in your project? And how they compare with Reactive frameworks or Kotlin Coroutines? Let's learn more about them and how they work!&lt;/p&gt;




&lt;h2&gt;
  
  
  Thread per Request model using platform threads
&lt;/h2&gt;

&lt;p&gt;In server applications, concurrent requests are generally handled independently of each other. It makes sense to use a thread per request, because it's easy to understand, to program and to debug. This means that for these types of server applications, the scalability depends of the number of threads that the server can start and the time they take to process each request.&lt;/p&gt;

&lt;p&gt;The JVM implements threads as wrappers arount the operative system threads. The problem is that OS threads (also called platform threads) are costly, which means that it's unfeasible to have millions of them. Even though the hardware resources support much more processing, that is limited by the number of threads that can be created.&lt;/p&gt;

&lt;p&gt;Platform threads are costly mainly because of two issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expensive creation&lt;/strong&gt; - involves allocating memory for the thread (~ 1 MB), initializing the thread stack and making OS calls to register the thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expensive context switching&lt;/strong&gt; - when there is a context switch, the OS has to save the local data and stack for the current thread and load new ones for the new thread. This involves many CPU cycles, as it requires loading and unloading the thread stack, which is not small (~ 1 MB).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsmjax53ehv477ee7d0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsmjax53ehv477ee7d0g.png" alt="Platform threads" width="721" height="291"&gt;&lt;/a&gt;&lt;br&gt;Each platform thread is mapped directly into an OS thread. Source: &lt;a href="https://blog.stackademic.com/java-threading-essentials-virtual-vs-platform-threads-explained-32365d8f92be" rel="noopener noreferrer"&gt;M K Pavan Kumar&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;Let's implement asynchronous requests to a blocking method &lt;code&gt;square(int i)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my machine, using the following code, I am able to create 100 000 platform threads and run the code in 5.39s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runWithPlatformThreads&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;threadNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;executorService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newCachedThreadPool&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadNumber&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

        &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;awaitTermination&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when trying to increase the number of platform threads to 200 000, it throws an OS error.&lt;/p&gt;

&lt;p&gt;How can this model be improved to be able to handle more requests concurrently?&lt;/p&gt;




&lt;h2&gt;
  
  
  Pooled threads
&lt;/h2&gt;

&lt;p&gt;Since the JVM is limited by the number of threads, another possibility is to use a thread sharing system instead of a thread per request. We can create a fixed number of threads and store them in a pool. When needed, a thread is fetched from the pool to start the calculation. After the calculation is done or if it is blocked, the thread is put back into the pool and released.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryuhgvclmi9be4gi6zf0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryuhgvclmi9be4gi6zf0.gif" alt="Thread pool model" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;There is a thread pool in green, with five threads. Each thread can perform a computation (red balls) and start another one when done. Source: &lt;a href="https://medium.com/@b.stoilov/everything-you-need-to-know-about-thread-pools-in-java-fe02e803d339" rel="noopener noreferrer"&gt;Borislav Stoilov&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;This model has the benefit of allowing for a much bigger number of concurrent operations with a smaller number of threads. However, it changes the programming style: the programmer needs to explicitly set a callback to complete the processing after the thread is blocked. These callbacks are regularly created as compositions of functions, or pipelines, and each may execute on a different thread.&lt;/p&gt;

&lt;p&gt;If you are wondering, that's how CompletableFuture works and how reactive frameworks are implemented. Let's take a look at those.&lt;/p&gt;

&lt;p&gt;The code below uses CompletableFutures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runWithCompletableFutures&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ExecutionException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;IntStream&lt;/span&gt; &lt;span class="n"&gt;intStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeClosed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapToObj&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toArray&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;[]::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All computations completed."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code does a similar calculation using the reactive framework WebFlux.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runWithWebFlux&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt; &lt;span class="n"&gt;latch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;IntStream&lt;/span&gt; &lt;span class="n"&gt;intStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeClosed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;boxed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromCallable&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribeOn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;boundedElastic&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collectList&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doOnTerminate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;latch:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;countDown&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All computations completed."&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code does a similar calculation using the reactive framework Mutiny.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runWithMutiny&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt; &lt;span class="n"&gt;latch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;IntStream&lt;/span&gt; &lt;span class="n"&gt;intStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeClosed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;Multi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createFrom&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;boxed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onItem&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;transformToUniAndConcatenate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Uni&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createFrom&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onTermination&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;latch:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;countDown&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All computations completed."&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with style of programming is that it breaks many of the JVM common patterns: stack traces can be hard to read, debuggers cannot step into the processing and reasoning the overall code is harder. It has a bigger learning curve because it does not seem "regular" Java code.&lt;/p&gt;

&lt;p&gt;How can we keep or improve the performance of pooled threads while preserving the thread-per-request model?&lt;/p&gt;




&lt;h2&gt;
  
  
  What about Kotlin Coroutines?
&lt;/h2&gt;

&lt;p&gt;The Kotlin language, which also runs on the JVM, found a way around the problem of concurrency: to use suspending functions, which is an abstraction similar to a thread, but that still uses thread pools under the hood. The coroutines can suspend their execution and later resume it in another thread. Instead of using the Platform Threads abstraction used by the JVM, the Kotlin language implemented a new framework that can be seen as light-weight threads, because they are very cheap to create and with minimal performance penalties.&lt;/p&gt;

&lt;p&gt;Let's see the same example implemented with Kotlin Coroutines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;runWithCoroutines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;threadNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;latch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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="n"&gt;num&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;threadNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All computations completed."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Coroutines, the user must explicitly suspend the code for the coroutine to block. We must divide our program into two parts: one based on non-blocking IO (suspending functions) and one blocking. This can be achieved using libraries based on Netty, but not every task is easiliy divisible in blocking and non-blocking IO. It's a challeging task and it requires work and experience do to correctly. While it can be great for advanced users, we lose again the simplicity we want in our programs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Virtual Threads
&lt;/h2&gt;

&lt;p&gt;It's impossible to implement OS threads more eficiently because they are used in different ways by many applications. However, it's possible to implement an abstraction in the JVM to create as many threads as the user wants, without them being mapped directly to OS threads. That abstraction is called virtual threads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhoz5l04gjg7inp8nmah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhoz5l04gjg7inp8nmah.png" alt="Virtual threads" width="721" height="438"&gt;&lt;/a&gt;&lt;br&gt;Each virtual thread is mapped to a platform thread, which in turn in mapped to OS thread. Source: &lt;a href="https://blog.stackademic.com/java-threading-essentials-virtual-vs-platform-threads-explained-32365d8f92be" rel="noopener noreferrer"&gt;M K Pavan Kumar&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;Virtual threads were first introduced in Java 19 as a Preview API, having its permanent status since Java 21. They are part of a bigger effort of OpenJDK, with Project Loom, to modernize Java concurrency model and improve high-throughput concurrent applications.&lt;/p&gt;

&lt;p&gt;One of the biggest improvements of virtual threads is that they only consume an OS thread while they perform calculations on the CPU. The result is that the scalability of the pooled threads is achieved transparently. When a blocking call is done, the virtual thread is suspended until resumed later. Another advantage from the developer point of view is that the virtual threads can be used just like the previous threads, with minimal changes in the code.&lt;/p&gt;

&lt;p&gt;The same example implemented with Virtual Threads. If you take a look at the Platform Threads example, you can see that the only change is in the executor service method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runWithVirtualThreads&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;threadNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;executorService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadNumber&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

        &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;executorService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;awaitTermination&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, this is the faster implementation when compared with all others presented before, just taking 1.41 s to create 100 000 threads and run the code (which contains a blocking call of 1s).&lt;/p&gt;

&lt;p&gt;In my machine, if I increase the thread number to 1 million, it takes 1.53s to run all threads, which is still faster than all other alternatives to calculate the same method but just 100 000 times. Impressive!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why shouldn't I refactor all my Platform Threads to Virtual Threads then?
&lt;/h2&gt;

&lt;p&gt;The advantage of Virtual Threads over Platform Threads is that it's possible to achieve higher concurrency, and with that, higher throughput. This makes virtual threads the ideal scenario for short-lived and short call stacks, for example for making an HTTP call or a database query. However, by itself, they do not make the application run faster. For CPU-intensive scenarios, virtual threads bring little to no improvement over platform threads, because since each virtual thread will be bound to a CPU core, they will perform identically to a platform thread.&lt;/p&gt;

&lt;p&gt;There is also a specific scenario where virtual threads can lead to worse performance, also known as the pinning issue. This happens when a virtual thread is "stuck" to an OS thread and cannot unmount it, monopolizing the OS thread. That can happen in two scenarions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronized methods&lt;/strong&gt; - If you have a synchronized method inside a Virtual Thread, the thread cannot suspend because it can create deadlocks. To avoid that, Reentrant locks can be used instead of synchronized methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native method call&lt;/strong&gt; - When making calls to a native library using JNI, for example.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Should I use virtual threads, a Reactive framework or coroutines?
&lt;/h2&gt;

&lt;p&gt;Virtual threads are ideal for short-lived and short call stacks, for example for making an HTTP call or a database query. If that's your scenario, they're probably the right fit. For CPU-bound processes, platform threads continue to be the best option.&lt;/p&gt;

&lt;p&gt;It's not expected that virtual threads totally replace Kotlin coroutines or reactive programming. For more complex projects, Kotlin coroutines offer much more flexibility than any other option, given its structured concurrency approach, channel-based concurrency and actor-based concurrency.&lt;/p&gt;

&lt;p&gt;Reactive frameworks are also best fit for projects that involve handling asynchronous data streams and event-driven architectures, with built-in features like backpressure and data stream processing operations, such as stream composition and transformation.&lt;/p&gt;

&lt;p&gt;If you want to know more about this topic, there are a few great resources about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/jeps/444" rel="noopener noreferrer"&gt;https://openjdk.org/jeps/444&lt;/a&gt; - The JEP where Virtual Threads are very thoroughly explained.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=wLJaCXzM6qk" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=wLJaCXzM6qk&lt;/a&gt; - great talk comparing Virtual Threads and Kotlin Coroutines.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.rockthejvm.com/ultimate-guide-to-java-virtual-threads/" rel="noopener noreferrer"&gt;https://blog.rockthejvm.com/ultimate-guide-to-java-virtual-threads/&lt;/a&gt; In-depth explanation about Virtual Threads.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.rockthejvm.com/kotlin-coroutines-101/" rel="noopener noreferrer"&gt;https://blog.rockthejvm.com/kotlin-coroutines-101/&lt;/a&gt; - In-depth explanation about Coroutines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>softwaredevelopment</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
