<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dmitry Kudryavtsev</title><description>All Articles</description><link>https://kudmitry.com/</link><item><title>Feature unrequest</title><link>https://kudmitry.com/articles/feature-unrequest/</link><guid isPermaLink="true">https://kudmitry.com/articles/feature-unrequest/</guid><pubDate>Wed, 11 Mar 2026 19:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, &lt;a href=&quot;https://ghostty.org/&quot;&gt;Ghostty&lt;/a&gt; updated itself.
I switched to Ghostty some time ago, as I like to try new stuff and find better tools for my workflow.
With this new update, I noticed something changed in Ghostty.
There are now 3 dots at the top of the window, in the center.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;New Ghostty &amp;quot;feature&amp;quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;388&quot; height=&quot;64&quot; src=&quot;/_astro/ghostty.CLZQ6JBB_XcLsV.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;New Ghostty &quot;feature&quot;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I bet most people would not event notice it, but I have been blessed with being a pedantic person, and these 3 dots hurt in my eyes.
I live in my terminal: I use NeoVim for coding, I write this blog from the terminal, I manage servers from the terminal, and I install applications from the terminal.
80% of my time at my computer is spent in the terminal (the other ~19% in Firefox).&lt;/p&gt;
&lt;p&gt;And so I asked Claude what’s up with the 3 dots, and Claude found this &lt;a href=&quot;https://github.com/ghostty-org/ghostty/pull/10090&quot;&gt;pull request&lt;/a&gt; which introduced split drag and drop.
Scrolling through GitHub, I tried to find a way to disable it.
I don’t use terminal tabs, drag and drop, or mouse interaction; I live in NeoVim + &lt;code&gt;tmux&lt;/code&gt;.
And I didn’t find any way to disable it.
I ended up commenting on &lt;a href=&quot;https://github.com/ghostty-org/ghostty/discussions/10108&quot;&gt;this issue&lt;/a&gt; where another user said:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;agreed. this should not even exist&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;to which one of the contributors replied:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Bold claim, as there’s no other way to move splits. A feature that was far more requested.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I never requested this.
I never wanted splits or a way to drag and drop them.
Now, sure, this is &lt;em&gt;someone else’s&lt;/em&gt; project, and they can develop it the way they see right.
But for a very long time I had this idea of “complete and usable software”.&lt;/p&gt;
&lt;p&gt;A complete and usable software, is a software that has reached its maturity and now is in maintenance mode, i.e. bug fixing and keeping up with updates to the host machine / OS / etc.
&lt;code&gt;tmux&lt;/code&gt;, for example, is a complete and usable software.
A terminal emulator, is a complete and usable software once it reaches a state where it can, well, &lt;em&gt;emulate&lt;/em&gt; video terminal, like Alacritty.&lt;/p&gt;
&lt;p&gt;But there is a fine line with complete and usable software, that if you keep pushing features, you enter the enshittification era — an era where your software goes into the rabbit hole of being too feature-rich to a point it is no longer usable by anyone.
You keep justifying it with “but users requested this feature”, yea well, the problem is that there is no “unrequest feature” option.
I liked Ghostty because it was a minimal terminal emulator that you would install and it worked out of the box.&lt;/p&gt;
&lt;p&gt;I don’t want to sound overdramatic, but I switched back to Alacritty.
Yes, these 3 dots killed my enthusiasm for this software.
A good software is a software that does its work and gets out of the way of the user.
Updates should be essentially invisible.&lt;/p&gt;
&lt;p&gt;Take iOS.
The iOS on my iPhone was good.
I got used to it, I got used to the layout, the design, and the controls.
But then, I got a new iPhone, and it came with Liquid Glass, and now I want to throw my iPhone away.
Despite the fact that I have upgraded from a 6-year-old iPhone, to a “better, more powerful, the best iPhone we have ever made”(c) (as of September 2023), the experience &lt;strong&gt;just sucks&lt;/strong&gt;.
The liquid ass design that I never wanted, with the slow and clunky interface.
I never wanted it.
So I would like to “feature unrequest” it.&lt;/p&gt;
&lt;p&gt;And I could keep going on and on about software that was feature complete, but had to invent features in order to justify future development, or simply because software engineers don’t like the term “maintenance”.
So everyone chases “feature requests”, but I’d argue that “features unrequests” should be as important as feature requests.&lt;/p&gt;</content:encoded></item><item><title>&quot;SaaS is Dead&quot; - they say</title><link>https://kudmitry.com/articles/saas-is-dead-they-say/</link><guid isPermaLink="true">https://kudmitry.com/articles/saas-is-dead-they-say/</guid><pubDate>Tue, 24 Feb 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;SaaS is Dead&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Everywhere you look — be it HackerNews, Reddit, or LinkedIn — &lt;em&gt;they&lt;/em&gt; claim that SaaS is dead.
Armed with a laptop and $100 Claude Code subscription, even your tech illiterate uncle can build the next Jira.
Producing a working product now costs pennies compared to what it costed before the AI boom.
But regardless of what &lt;em&gt;they&lt;/em&gt; say, I’m here to tell you that &lt;em&gt;they&lt;/em&gt; — are &lt;strong&gt;full of shit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You see, I have been working as a software engineer from 2009.
Moreover, I was building products (most of them failed), since high-school, and today I still build and maintain &lt;a href=&quot;/projects&quot;&gt;some of them&lt;/a&gt;.
I have been observing many products, and founders, and I came to a simple conclusion: the people who say that SaaS is dead — never actually built anything themselves.&lt;/p&gt;
&lt;p&gt;They never shipped a working product end-to-end.
Never had to debug a production issue that costed them real money and customers, as opposed to this cost being absorbed by their employer, while they are, &lt;em&gt;generally&lt;/em&gt;, protected against personal liability.
They never had to charge customers, issue refunds and maintain their merchant reputation, provide customer support — sometimes to very annoying people, do marketing and advertisement, worry about accounting and compliance, etc.&lt;/p&gt;
&lt;p&gt;But more importantly, they were never sitting there, thinking to themselves &lt;em&gt;“what the fuck am I doing with my life”&lt;/em&gt;, while wondering will they ever be able to make it?
Will they be able to wake up one day, look at themselves in the mirror and say &lt;em&gt;“I am proud of what I have achieved”&lt;/em&gt;, rather than thinking that all they have managed to achieve was wasting their precious time on a stupid SaaS idea?&lt;/p&gt;
&lt;p&gt;They did none of this.
Instead, all they had to do is write a nice article titled “SaaS is Dead”.&lt;/p&gt;
&lt;p&gt;Software was never the end-goal.
Developers convinced themselves that it is, but it never was.
The end goal was to build a product and sell it.
Make money.
Spin the capitalistic wheel.
Or simply build a good product and stop participating in capitalistic society all-together.&lt;/p&gt;
&lt;p&gt;Most people are not built for entrepreneurship.
They simply are not ready to give up their weekends for a prospect of 5% chance of reaching a &lt;em&gt;sustainable&lt;/em&gt; profit from their SaaS.
The math is right here, and nobody argues with it: it is better to a get a cushy 9-to-5 in tech with pension and benefits, than to pursue solopreneurship / building a SaaS, while making pennies.
Even if you land in the 5%, you will be making &lt;em&gt;sustainable&lt;/em&gt; profit, with the keyword being &lt;strong&gt;sustainable&lt;/strong&gt;.
Exceptions exist of course, but I doubt that if you have a hard time swallowing 5%, you’d be happy to devour sub 1% chance for being exceptional.&lt;/p&gt;
&lt;p&gt;And I don’t blame them.
I don’t think they are worse people, or that we don’t need them.
On the contrary.
It’s unsustainable if everyone is an entrepreneur.&lt;/p&gt;
&lt;p&gt;There is an epidemic of “lack of doing”.
People don’t want “doing”, they want “learning”.
They are afraid to act.&lt;/p&gt;
&lt;p&gt;We all know that in order to have a healthy life, we need at least two and a half hours of physical activity per week.
And yet, most people spend more time finding the &lt;em&gt;best workout routine&lt;/em&gt;.
People get obsessed with &lt;em&gt;productivity&lt;/em&gt;, spending countless hours optimizing their Notion templates, for the sake of completing a task 1 minute earlier than their peers, once every 3 months.
And they do the same with entrepreneurship.
Instead of going and building, they keep &lt;em&gt;researching&lt;/em&gt; or coming up with excuses like &lt;em&gt;“it is no longer viable to build a SaaS”&lt;/em&gt;.
Where is all the “10x productivity” that we keep hearing about?
Where are all the Jira replacements?
I will tell you where.&lt;/p&gt;
&lt;p&gt;It’s all there, buried in GitHub repos that people quickly created with the help of AI, but when they realized that this was the &lt;em&gt;easy&lt;/em&gt; part, and now their sprint has turned into a life-long marathon, now, now they quit — leaving behind a graveyard of slopware.
Because they never had it.
They never had a reason to pursue entrepreneurship.
They were always comfortable with what they had, instead trying to fill the void with each and every new toy our capitalistic society has produced.
Everything else is too hard.
There is no manual for it.
It comes with a shitload of boring work, that people either grind through or contract, for money, to someone else.
You need a strong &lt;em&gt;why&lt;/em&gt; if you want to pursue entrepreneurship, and no, money is not a good &lt;em&gt;why&lt;/em&gt;.
And most people simply don’t have it.
And it’s fine.&lt;/p&gt;
&lt;p&gt;So no, SaaS is not dead.
Your uncle, unless he has an unexplainable desire for entrepreneurship, will probably never ship anything.
He might create something, brag about it during your extended family gathering, while everyone else will be in awe.
But in one year, when you ask him &lt;em&gt;“Hey Uncle, what’s going on with that business idea you had a year ago?”&lt;/em&gt;, he will shrug, look you in the eyes, and say… &lt;em&gt;“What business idea?”&lt;/em&gt;.&lt;/p&gt;</content:encoded></item><item><title>Farewell, Rust</title><link>https://kudmitry.com/articles/farewell-rust/</link><guid isPermaLink="true">https://kudmitry.com/articles/farewell-rust/</guid><pubDate>Thu, 19 Feb 2026 18:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;prelude&quot;&gt;Prelude&lt;/h2&gt;
&lt;p&gt;When I was in 9th grade — last year before high-school — my best friend persuaded me to join, together with him, the schools programming club.
At first, I hesitated, but later agreed.
I am immensely thankful to him for this.&lt;/p&gt;
&lt;p&gt;There, step by step, we learned Pascal using Turbo Pascal.
Little by little, we grasped the basics of the language: variables, operators, string manipulations, data structures — until eventually, remaking &lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life&quot;&gt;Conway’s Game of Life&lt;/a&gt;.
And then, summer break came.
Other than HTML, which is not a programming language, Pascal was the first real programming language I have learned and used.&lt;/p&gt;
&lt;p&gt;But I did not stay with Pascal for very long time.
After the summer break, in high-school, I have chosen the “Software Engineering” branch of studying, and there we learned C.
Just like Pascal, we started step by step: basic operators, string manipulations, memory allocation, data structures, and the final boss, the &lt;code&gt;void *&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I fell in love with C.
The precise control over memory; passing variables by reference or pointer; the need to allocate memory for every data structure that you wanted to create.&lt;/p&gt;
&lt;p&gt;After 3 more years of studying, I have graduated high-school.
In that time, I have improved my C skills, learned some PHP, picked up C++, and tried to build a variety of programs (don’t judge me too hard, these are ~20 years old): &lt;a href=&quot;https://github.com/skwee357/TankSmasher&quot;&gt;a clone of BattleCity&lt;/a&gt;, &lt;a href=&quot;https://github.com/skwee357/gr&quot;&gt;3D software renderer&lt;/a&gt;, &lt;a href=&quot;https://github.com/skwee357/irc-bot&quot;&gt;IRC bot&lt;/a&gt;, &lt;a href=&quot;https://github.com/skwee357/wrap-os&quot;&gt;unfinished operating system kernel&lt;/a&gt;, &lt;a href=&quot;https://github.com/skwee357/sk-graphics-engine&quot;&gt;unfinished game engine&lt;/a&gt;, &lt;a href=&quot;https://github.com/skwee357/sk_glft&quot;&gt;TTF renderer for OpenGL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After high-school, I have enrolled in a two-year college program that would earn me a Practical Software Engineering Degree.
During the summer break between the first and the seconds years, I had a choice to make: go work in McDonald’s (I had experience working as a waiter during high school summer breaks), or find a job in software engineering.
After 2 seconds of hesitation, I have crafted a CV and started to send it to every position I saw online.
And despite the fact that I really wanted to get a software development position in C or C++, nobody would hire me.
Eventually, I have secured a web-development position in PHP (thank you very much my first employer for giving me a chance).&lt;/p&gt;
&lt;p&gt;And this would be the last time I’d touch C or C++.
Dynamic, high-level languages such as PHP, Python, and Ruby — are more suited to the dynamic nature of web development.
You rarely need to squeeze the maximum performance from your hardware, since for every second you gain by optimizing data structures allocations in C, you lose 10x more waiting for network or disk requests to resolve.
And so, collectively, we all agreed that the web is better to be written in dynamic languages.&lt;/p&gt;
&lt;p&gt;But just like your first true-love, C would hunt me.
I would obsess about micro-optimizations, and would get mad that I can’t control when variables are allocated, and how to pass things by reference or pointer without making a redundant copy.
And then, Rust became a thing.&lt;/p&gt;
&lt;h2 id=&quot;web-development-in-rust&quot;&gt;Web development in Rust&lt;/h2&gt;
&lt;p&gt;Just like every software engineer, I started learning Rust by building my own game engine.
Do you even call yourself a software engineer if you never tried to build your own game engine?
And I fell in-love with Rust.
C was aging, and failed to catch up with the moving world of hipster development: modern tooling, linters, formatters, package management.
Rust offered the best of both worlds: low-level control of memory allocation, and variable life-cycle, while having modern development practices: a simple-to-use compiler, built-in linter and formatter, and of course a modern package management tool and repository.&lt;/p&gt;
&lt;p&gt;The community was loving Rust, and the language was growing very fast.
And for the first time you could write a web application using a low-level systems-programming language. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
After picking up the basics of the language, and abandoning my game engine, I have decided to build a web application in Rust.
And, in the end of 2023, &lt;a class=&quot;internal new&quot; href=&quot;/articles/building-a-webapp-in-rust/&quot;&gt;I have succeeded&lt;/a&gt;.
I have built, and shipped a fully operational web application that was making me money, in Rust.&lt;/p&gt;
&lt;p&gt;Throughout the years, I learned from my mistakes, and shared my progress &lt;a class=&quot;internal new&quot; href=&quot;/articles/webapp-localization-in-rust/&quot;&gt;here&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/serving-astro-with-rust/&quot;&gt;here&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/one-year-of-rust-in-production/&quot;&gt;and here&lt;/a&gt;.
Eventually, the application became a Frankensteins’ monster were the backend would be pure Rust, while the frontend was a statically generated Astro website.
Until, in the end, I have reached a road-block and could no longer maintain or extend the application.&lt;/p&gt;
&lt;p&gt;I took a painful decision to migrate everything to Node.js, and conclude this experiment with Rust.
It was hard for me to make this decision.
I really loved working with Rust, and I wanted to make it work.
My work with Rust earned me valuable human connections, and opportunities to present my knowledge at two conferences, and one meetup.
I am happy that I have took the risk with Rust, but…&lt;/p&gt;
&lt;h2 id=&quot;farewell-rust-at-least-for-now&quot;&gt;Farewell Rust, at least for now…&lt;/h2&gt;
&lt;p&gt;I’m not going to go over the positive sides of Rust, since I covered them in other articles such as &lt;a class=&quot;internal new&quot; href=&quot;/articles/building-a-webapp-in-rust/&quot;&gt;Building a web app in Rust&lt;/a&gt; and &lt;a class=&quot;internal new&quot; href=&quot;/articles/one-year-of-rust-in-production/&quot;&gt;One year of Rust in production&lt;/a&gt;.
But I do want to talk about some of the struggles I had, both as a reminder to my future self (you can take C from me, but you can’t take my love to low-level programming), and as a cautionary tale to everyone else.&lt;/p&gt;
&lt;p&gt;Keep in mind that everything I share is based on my perspective: I’m a solo founder, building and running a web application.
There is no heavy workload like video processing, nor a need to have low latency — two things at which Rust excels.
From my experience, and my product, the bottleneck is always the database, disk, or network.
I’m also, somewhat, experienced in Rust, so I’m not the “&lt;em&gt;I tried to build a web app in Rust, but I don’t understand borrow checker, so Rust bad&lt;/em&gt;” kind of guy.
Now, with that out of the way, let’s begin.&lt;/p&gt;
&lt;h3 id=&quot;templating&quot;&gt;Templating&lt;/h3&gt;
&lt;p&gt;As I mentioned earlier, from pure Rust + server side generated templates with &lt;code&gt;tera&lt;/code&gt;, I have switched to a Rust API server + Astro static website that calls the API.
The reason I moved away from rendering HTML in Rust, is the fact that I am too spoiled with type-safe templates.
With Astro, I can use the &lt;code&gt;.astro&lt;/code&gt; components that are type-safe.
Since I use, and advocate for, Typescript, all my templates are type-safe, and there is very little chance to mistype a variable, or miss it.
If you have a component like this, it’s very hard to make it compile with the wrong type (unless you do stupid stuff like &lt;code&gt;props as any&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  username&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; UserCard&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      Hello &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;username&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With libraries like &lt;code&gt;tera&lt;/code&gt;, &lt;code&gt;handlebars&lt;/code&gt;, or &lt;code&gt;mrml&lt;/code&gt; — your view is essentially separated from the props, and if you rename a field in the template, you must remember to rename it in the model as well.
It’s possible to solve it with tests at the cost of longer CI/CD pipeline, but I will talk about compilation time a bit later.
Functions inside templates are basically wild-west of strings.
And you need functions.
It’s hard to make rich templates without functions.&lt;/p&gt;
&lt;p&gt;But there are libraries like &lt;code&gt;maud&lt;/code&gt; or &lt;code&gt;askama&lt;/code&gt; — I hear you say.
And I agree, there are.
These are essentially type-safe HTML templates that use the macro mechanism from Rust.
By building, basically, HTML DSL using Rust macro, you can create compile time, type-safe templates.
But then again, compilation is expensive, but more on that later.&lt;/p&gt;
&lt;h3 id=&quot;localization-and-internationalization&quot;&gt;Localization and Internationalization&lt;/h3&gt;
&lt;p&gt;I have been talking about localization &lt;a class=&quot;internal new&quot; href=&quot;/articles/a-different-approach-for-localizing-react-js-app/&quot;&gt;back in 2015&lt;/a&gt;.
Node.js ships with full &lt;code&gt;icu&lt;/code&gt; support, and a set of &lt;code&gt;Intl.*&lt;/code&gt; APIs to format numbers, lists, currencies, country names, you name it.
On top of that, with libraries like &lt;code&gt;i18next&lt;/code&gt;, one can get type-safe auto-complete for translations, something that I was not able to achieve in Rust.
Yes, Rust has support for &lt;code&gt;fluent&lt;/code&gt; translation files developed by Mozilla, and a minimal support for number formatting.
But Node.js has everything you need in order to build and ship fully localized and translated web-applications.
It’s a know fact that &lt;code&gt;i18n&lt;/code&gt; is lacking in Rust (see: &lt;a href=&quot;https://www.arewewebyet.org/topics/i18n/&quot;&gt;AWWY: Internationalization&lt;/a&gt;), and bindings for &lt;code&gt;icu4c&lt;/code&gt; are in progress, but they are nowhere near what Node.js can offer it that regard.&lt;/p&gt;
&lt;h3 id=&quot;the-web-is-dynamic-by-nature&quot;&gt;The web is dynamic by nature&lt;/h3&gt;
&lt;p&gt;Take it or leave it, but the web is dynamic by nature.
Most of the work is serializing and deserializing data between different systems, be it a database, Redis, external APIs, or template engines.
Rust has one of the best (de)serialization libraries in my opinion: &lt;code&gt;serde&lt;/code&gt;.
And yet, due to the nature of safety in Rust, I’d find myself writing boilerplate code just to avoid calling &lt;code&gt;.unwrap()&lt;/code&gt;.
I’d get long chain calls of &lt;code&gt;.ok_or&lt;/code&gt; followed by &lt;code&gt;.map_err&lt;/code&gt;.
I defined a dozen of custom error enums, some taking other enums, because you want to be able to handle errors properly, and your functions can’t just return any error.&lt;/p&gt;
&lt;p&gt;Similar thing can be said about writing SQL.
I was really happy with using &lt;code&gt;sqlx&lt;/code&gt;, which is a crate for compile-time checked SQL queries.
By relying on macros in Rust, &lt;code&gt;sqlx&lt;/code&gt; would execute the query against a real database instance in order to make sure that your query is valid, and the mappings are correct.
However, writing dynamic queries with &lt;code&gt;sqlx&lt;/code&gt; is a PITA, as you can’t build a dynamic string and make sure it’s checked during compilation, so you have to resort to using non-checked SQL queries.
And honestly, with &lt;code&gt;kysely&lt;/code&gt; in Node.js, I can get a similar result, without the need to have a connection to the DB, while having ergonomic query builder to build dynamic queries, without the overhead of compilation time.&lt;/p&gt;
&lt;p&gt;Okay, I hear you, let’s talk about compilation time.&lt;/p&gt;
&lt;h3 id=&quot;compilation-time&quot;&gt;Compilation time&lt;/h3&gt;
&lt;p&gt;Rust achieves its safety at the expense of, somewhat long, compilation time.
On modern hardware, the compilation time is not that bad.
It’s not good enough to have uninterrupted change-f5-preview cycle, but it’s good enough especially with incremental compilation.&lt;/p&gt;
&lt;p&gt;However, the more crates you have, the more macros you use, the slower the compilation time becomes.
So the problem becomes is how to achieve fast compilation during CI/CD.
Due to the dynamic nature of the web, you often times find yourself in a loop of: there is an error in production → fix it → deploy.
And you want this loop to be as fast as possible, because you have customers who can’t do things with your app.&lt;/p&gt;
&lt;p&gt;I have my own hardware to run CI/CD workers.
A dedicated VM on an Intel Core i5-7500 with 32GB of RAM.
The VM has access to all 4 CPU cores, and would take about 14 minutes from push to &lt;code&gt;master&lt;/code&gt; until the docker container was deployed on the server (about 12 of it is the docker stage with compilation).
That’s with multistaged docker file, and a cached builder layer; with no cache, it would probably take ~20-25 minutes.
And it does not include running tests or &lt;code&gt;clippy&lt;/code&gt; as part of the CI/CD.
I simply gave-up trying to set-up proper caching so tests, &lt;code&gt;clippy&lt;/code&gt;, and compilation would be able to use the same cache.&lt;/p&gt;
&lt;p&gt;Node.js, on the other hand, takes on average 5 minutes, including linting, and tests.
And since I moved to Node.js, I added another back-office service, so I deploy more code, 3 times faster, while actually relying on running tests and lint as part of the CI/CD pipeline.&lt;/p&gt;
&lt;h3 id=&quot;ecosystem-maturity&quot;&gt;Ecosystem maturity&lt;/h3&gt;
&lt;p&gt;Rust has a very mature ecosystem in most aspects, but it is lacking in the web aspect.
Need an obscure third party API?
It’s probably not there.
So you end up implementing APIs instead of doing core business logic.
Each API is additional code that you need to test and maintain.&lt;/p&gt;
&lt;p&gt;It’s not hard to implement REST APIs, but the lack of maturity does slow you down.
I had to implement, and test, third party APIs, queue mechanism on top of PostgreSQL, code to validate webhook signatures.
It was fun, but it comes out of the box in other, “standard”, languages.&lt;/p&gt;
&lt;h3 id=&quot;nodejs-is-good-enough&quot;&gt;Node.js is good enough&lt;/h3&gt;
&lt;p&gt;And honestly, Node.js is good enough.
People like to criticize Node.js or the &lt;code&gt;npm&lt;/code&gt; ecosystem, implying that there is something fundamentally wrong with JavaScript.
And sure, JavaScript is far from being a good language.
There are many quirks in the language, which you can learn only with experience, pain, and tears.
And I miss stuff from Rust like the &lt;code&gt;Result&lt;/code&gt; and &lt;code&gt;Option&lt;/code&gt; types; I miss the &lt;code&gt;match&lt;/code&gt; statement; and I miss the &lt;code&gt;enum&lt;/code&gt; as well.
But in all honesty, Node.js ecosystem has matured and is stable to write web applications.
You have libraries like &lt;code&gt;zod&lt;/code&gt; to validate request/response JSONs, libraries like &lt;code&gt;kysely&lt;/code&gt;, &lt;code&gt;@kitajs/html&lt;/code&gt;, and my own &lt;a class=&quot;internal new&quot; href=&quot;/articles/mjmx-jsx-runtime-mjml/&quot;&gt;@mjmx/core&lt;/a&gt; to write type-safe SQL, HTML, and MJML.
And &lt;code&gt;async/await&lt;/code&gt; is still better in Node.js than in Rust.
It’s always weird to need to bring thirds party &lt;code&gt;async-trait&lt;/code&gt; crate because you want to have an &lt;code&gt;async&lt;/code&gt; method in a &lt;code&gt;trait&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are still issues with Node.js.
The never-ending deprecation of &lt;code&gt;cjs&lt;/code&gt; in favor of &lt;code&gt;esm&lt;/code&gt;; it’s mostly good, until you encounter a &lt;code&gt;cjs&lt;/code&gt; package, and then it’s hell.
The cumbersome &lt;code&gt;eslint&lt;/code&gt; and &lt;code&gt;prettier&lt;/code&gt; setup where you need to juggle between 2 files and dozen of plugins to get basic functionality you get with &lt;code&gt;clippy&lt;/code&gt; out-of-the-box; I wait for the day &lt;code&gt;biome&lt;/code&gt; will close the gaps, and becomes the standard tool.
Workspaces are still not solved, although &lt;code&gt;pnpm&lt;/code&gt; is better in this regard, but it’s nowhere near the workspaces in &lt;code&gt;cargo&lt;/code&gt;.
And the occasional struggles with typescript where the runtime seems to be changing too often; is it &lt;code&gt;ts-node&lt;/code&gt;? &lt;code&gt;tsx&lt;/code&gt;? &lt;code&gt;tsm&lt;/code&gt;? The built-in typescript runtime in &lt;code&gt;node&lt;/code&gt;? &lt;code&gt;deno&lt;/code&gt;? &lt;code&gt;bun&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Oh, and I really wish we all would just agree that &lt;code&gt;snake_case&lt;/code&gt; is simply the best, most aesthetically pleasing, and easy to read case for programming.
I really hate the fact that JavaScript has adopted the &lt;code&gt;camelCase&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;closing-notes&quot;&gt;Closing notes&lt;/h2&gt;
&lt;p&gt;Building solo is hard.
You wear 10 hats as software engineer, in addition to the dozen hats as an entrepreneur.
I really wanted Rust to succeed, but I also need to move forward.
I found myself ignoring bugs in Sentry because it meant going back to long compile times.
I was postponing feature development because it meant slow iteration speed, and trying to synchronize backend REST API with frontend, or the need to visually re-test every page after a variable rename in a template.
Call me spoiled, but I don’t have the luxury of development team, and someone checking my code.&lt;/p&gt;
&lt;p&gt;Rust excels at non-visual things, i.e. things you can write and wrap with tests.
But when you need to develop UI, it becomes painful.
You wait for the code the recompile; worried about passing the wrong variable to a view.&lt;/p&gt;
&lt;p&gt;From pure Rust, I had to go to Rust with a statically generated HTML + Alpine on top of it.
To make sure my email templates are safe, I was considering writing a dedicated mailing service in NodeJS, just so I could use &lt;code&gt;react.email&lt;/code&gt; (or my own &lt;code&gt;@mjmx/core&lt;/code&gt;), and get &lt;em&gt;some&lt;/em&gt; type-safety.
It’s as if I was moving backwards, breaking my Rust monolith and rewriting parts in a dynamic language that is better suited for the web.&lt;/p&gt;
&lt;p&gt;No, Node.js is not perfect.
But at least I have one stack, and funny enough, I get more type-safety than I had in Rust.
Sure, it’s a fake type-safety, JavaScript is a dynamic language, but I no longer have to jump between my view files and my model and try to keep them in sync.
I no longer misspell translation keys, resulting in blank words; emails do not come with &lt;code&gt;Hello {{dearCustomer}}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It seems like most of my issues boil down to dynamic things: templates, i18n, SQL.
If I were to write an API service today, I would probably choose Rust again, since API services do not have to deal with views or translations.
I still don’t know how I would handle SQL though.
ORMs are not my cup of tea, and other than &lt;code&gt;sqlx&lt;/code&gt;, Rust seems to lack a good type-safe query builder.&lt;/p&gt;
&lt;p&gt;I also miss the small footprint of the application.
With Rust, the containers would use between 60 and 80 MB of RAM; but with Node.js, the lowest I have is 117 MB of RAM for the back-office, without any load.
And it’s true what they say: use the right tool for the job.
Rust shines in CPU-heavy tasks, and for sure I will be using it when I will have such tasks.&lt;/p&gt;
&lt;p&gt;But until then, farewell Rust.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Yes, there is Go. But I never really had the chance to like Go. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Don&apos;t apologize for replying late to my email</title><link>https://kudmitry.com/articles/dont-apologize-for-replying-late-to-my-email/</link><guid isPermaLink="true">https://kudmitry.com/articles/dont-apologize-for-replying-late-to-my-email/</guid><pubDate>Wed, 18 Feb 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before the smart-phone arrived — together with widely-available GSM networks — online communication was different.
I would deliberately have to sit in-front of my computer, connect to the internet (in the era of dial-up), and launch my favorite instant-messaging app: ICQ, MSN, Miranda, Trillian, Pidgin, or an IRC client.
I’d spend some time, limited in the era of dial-up, to chat with my friends (or online strangers), &lt;strong&gt;if&lt;/strong&gt; they happened to be online at the same time.
Of course, I also used the phone, the one with the physical wires, but this post is not about it.&lt;/p&gt;
&lt;p&gt;And when the internet was moving at the speed of light, from DSL to ADSL to Broadband, and the web was maturing from 1.0 to 2.0 — we got the mobile phone and Facebook Messenger.
Suddenly, intentional communication was replaced with &lt;strong&gt;instant&lt;/strong&gt; communication.
You’d be expected to reply no matter the time of day or whether you were near a computer or not, because you owned a smartphone, and were connected 24/7.&lt;/p&gt;
&lt;p&gt;Slow communication was replaced with instant communication, and even in business environment you’d rely more on tools like Slack rather than email.
And while it did bridge the gap for certain teams, for example remote or distributed teams who now could operate as if they were all present in the same room, it also created a sense of urgency for every little thing.
Sending a message is easy, replying is not.
In order to reply, you need to stop what you are doing, read and analyze the content, and then craft a reply.
Getting back to what you were doing before, wastes a lot of energy and time (I wrote about context switch back in 2019 &lt;a class=&quot;internal new&quot; href=&quot;/articles/context-switch-the-only-operation-that-kills-productivity/&quot;&gt;here&lt;/a&gt;).
And so, naturally, I have disabled all phone notifications.
No more sound, no more blinking notification lights.&lt;/p&gt;
&lt;p&gt;But the world kept moving, and soon short form content took over the world.
I never cared for social networks like Twitter.
But after &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;burning out&lt;/a&gt; of tech, selling everything I have, and becoming a &lt;a href=&quot;/categories/digital-nomad/&quot;&gt;digital nomad&lt;/a&gt;, I opened the world of indie-hacking (jeez, &lt;a class=&quot;internal new&quot; href=&quot;/articles/on-buildinpublic-and-indiehackers/&quot;&gt;I hate&lt;/a&gt; this term), and, of course, all indie-hackers were hanging out on Twitter.
And so I started to use Twitter, and LinkedIn, and Instagram, and my brain melted.&lt;/p&gt;
&lt;p&gt;Until eventually, I have deleted all my social network accounts, and came back to the source — the email!
And little by little I started to build connections with people over emails.
I would email interesting people whose blogs I read; and I would get emails from people who read my blog(s).
We would exchange ideas, and discuss various topics.&lt;/p&gt;
&lt;p&gt;But most of the emails I get from people usually start with a variant of “Apologies for the long delay in my reply”.
And, first of all, I think people should apologize if they harmed someone, or were wrong — which is not case if you did not reply to my email “on time” (whatever this means).
But, second of all, you should not apologize for not replying to my email “on time” (again, whatever this means).&lt;/p&gt;
&lt;p&gt;The reason I use email is that I want to be able to dedicated my full attention (to the extent possible in this stupid, crazy world) to your message, and craft a thorough reply.
I avoid Instant Messaging with close people for the same reason; I prefer to meet offline, if possible, or do a video call where I’m focused only on the call.
IM is for short stuff like “running late”, “grab the cake”, etc.&lt;/p&gt;
&lt;p&gt;And email is the antidote for this crazy world.
Email is slow for a reason; it has no “seen” status for messages; there is no online status for the recipient.
You send it, and you forget it.
Sure, in professional settings, you expect to get a reply, but between two online strangers?
No way.&lt;/p&gt;
&lt;p&gt;So, please don’t apologize for replying late to email.
You don’t have time?
Take as long as you need.
I have conversation with people where we reply once a month.&lt;/p&gt;
&lt;p&gt;You feel like you don’t like me, or don’t want to reply?
Then don’t!
It’s email.
It’s slow.
And it’s the antidote to fast-paced slop, and a place to have interesting discussions, because you reply to it deliberately, rather than because of FOMO or notification fatigue.&lt;/p&gt;</content:encoded></item><item><title>Introducing mjmx: a custom JSX runtime for mjml</title><link>https://kudmitry.com/articles/mjmx-jsx-runtime-mjml/</link><guid isPermaLink="true">https://kudmitry.com/articles/mjmx-jsx-runtime-mjml/</guid><pubDate>Fri, 06 Feb 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my opinion, one of the best things that happened &lt;em&gt;because&lt;/em&gt; of React, is JSX.
I know many people seem to hate on JSX, but I like it.
With JSX, or more specifically TSX, one can write typesafe html, instead of relaying on libraries like &lt;code&gt;handlebars&lt;/code&gt;, which is a great library, but it keeps your code and your variables separated.
So every change in variables needs to be synced to the view-model, or worse, risk in runtime errors / blank values.&lt;/p&gt;
&lt;p&gt;Recently, I’ve been doing some server-side rendering, and was using &lt;a href=&quot;https://github.com/kitajs/html&quot;&gt;&lt;code&gt;@kitajs/html&lt;/code&gt;&lt;/a&gt; as HTML JSX runtime.
&lt;code&gt;@kitajs/html&lt;/code&gt; is great, because it does not bring &lt;code&gt;react&lt;/code&gt; or &lt;code&gt;react-dom&lt;/code&gt; with it.
And when I got to emails, I decided to try &lt;code&gt;react-email&lt;/code&gt; and &lt;code&gt;mjml-react&lt;/code&gt;.
But I was disappointed in both.&lt;/p&gt;
&lt;p&gt;First of all, I like &lt;code&gt;mjml&lt;/code&gt;.
It’s a great syntax, and it’s battle tested.
&lt;code&gt;react-email&lt;/code&gt; seems to not use &lt;code&gt;mjml&lt;/code&gt;, but instead re-implement email rendering themselves.
Now, &lt;code&gt;react-email&lt;/code&gt; is a product by Resend, and the guys at Resend are doing email sending, so I bet they know how to render emails properly.
Despite this, there is a second issue: I don’t like React, and I don’t want to bring &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; just to render a couple of emails.&lt;/p&gt;
&lt;p&gt;So I decided to try to write a custom JSX runtime for &lt;code&gt;mjml&lt;/code&gt;.
And that’s how &lt;code&gt;@mjmx/core&lt;/code&gt; was born: &lt;a href=&quot;https://github.com/skwee357/mjmx&quot;&gt;mjmx Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s a custom JSX runtime that has no dependency on &lt;code&gt;react&lt;/code&gt; or &lt;code&gt;react-dom&lt;/code&gt;.
It implements the minimum needed runtime to support all the &lt;code&gt;mjml&lt;/code&gt; tags, as well as the allowed HTML tags.
And with it, writing &lt;code&gt;mjml&lt;/code&gt; becomes a breeze:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Email&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;mjml&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-section&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-column&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-text&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; font-size&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;20px&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; color&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;#333&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            Hello &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-text&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-button&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;Click me&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-button&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-column&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-section&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mj-body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;mjml&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;errors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Email&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;World&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Under the hood, &lt;code&gt;render&lt;/code&gt; is just a proxy to &lt;code&gt;mjml2html&lt;/code&gt; from the &lt;code&gt;mjml&lt;/code&gt; library.
This means that &lt;code&gt;mjml&lt;/code&gt; compilation happens with each email you render.
I would like to outsource the &lt;code&gt;mjml&lt;/code&gt; compilation into a build / transpile step, but with the current approach it is not possible, unless you decide to re-implement email rendering yourself.&lt;/p&gt;
&lt;p&gt;I hope you will find this library useful.
Don’t forget to star it on GitHub, and feel free to reach out to me for questions, or open an issue / PR for suggestions and improvements.&lt;/p&gt;</content:encoded></item><item><title>We can no longer trust software</title><link>https://kudmitry.com/articles/we-can-no-longer-trust-software/</link><guid isPermaLink="true">https://kudmitry.com/articles/we-can-no-longer-trust-software/</guid><pubDate>Mon, 26 Jan 2026 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you told someone back in early 2000s that they would be able to update the software on their TV or fridge, they would look at you very confusingly.
And yet, here we are.&lt;/p&gt;
&lt;p&gt;As I was powering my LG “smart” TV a few months ago, I was greeted with a message telling me that there is a new update available.
The thing with TV updates is that they always have bad timing.
I turned the TV on to watch a movie, not to wait 7 minutes until it updates and restarts itself, while my popcorn is getting cold.
And so, I declined the update.
Little I knew that I dodged a bullet there.&lt;/p&gt;
&lt;p&gt;Apparently, this update introduced Microsoft Copilot&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, which &lt;a href=&quot;https://www.tomshardware.com/service-providers/tv-providers/lg-tv-update-adds-non-removable-microsoft-copilot-app-to-webos&quot;&gt;can not be removed&lt;/a&gt;.
Moreover, the same update included a feature called “Live Plus” which does Automatic Content Recognition (i.e. analyze what you watch), sell it to LG Ad Solutions (yes, ad as in “advertisement”), and create a “viewer profile” of you, in order to…
Go on, guess…
Yes!
Serve. You. ADS.&lt;/p&gt;
&lt;p&gt;After that, I have disconnected my TV from the internet.
I use an Android TV box anyway, and unless we find a way to update our TVs from 4k to 8k via software, or upgrade from 50” to, say, 70”, I’m done with software updates of my appliances.&lt;/p&gt;
&lt;p&gt;I guess smartphones, and broadband internet, has accustomed us to get updates in real-time the moment they appear.
Back in the “old days”, I would download a new version of &lt;a href=&quot;https://en.wikipedia.org/wiki/Miranda_NG&quot;&gt;Miranda&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Winamp&quot;&gt;Winamp&lt;/a&gt;, only if my current version had issues that I was aware of.
But today, most devices seem to update themselves in the background, and I got “&lt;em&gt;the Wi-Fi symbol&lt;/em&gt;” blinking on my fridge and my washing machine, both of them are begging me to connect them to the internet so I can monitor… how many avocados are left in the fridge?!&lt;/p&gt;
&lt;p&gt;Jeff Geerling wrote almost a year ago, how he &lt;a href=&quot;https://www.jeffgeerling.com/blog/2025/i-wont-connect-my-dishwasher-your-stupid-cloud/&quot;&gt;bought a dishwasher that required cloud subscription to work&lt;/a&gt;.
Automakers tried to pay-wall features like heated seats behind a subscription, despite the fact that the heating element is installed in every, fucking, car.
And yet, to use it, you have to pay a subscription.
Predatory business 101.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;This initiative, popularized by BMW, seems to have died.
But have no fear, there are rooms full of “smart business people”, who brainstorm day-and-night, how to bring it back in a different packaging.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And so, my conclusion is that we no longer can trust software.&lt;/p&gt;
&lt;p&gt;In the past, you used to buy an appliance, and kind-of commit to a known feature set, since it was mostly impossible to update the software, because the software was actually called firmware, and was embedded into the microchip of the appliance.
Today, every appliance is basically a computer.
Your TV, dishwasher, fridge, and car — all run some kind of Linux with custom UI on top of it.
And by connecting them to the internet, you essentially give the maker of said appliance a control over how this appliance will function.
So I’d argue that it is better to avoid “internet connected” appliances all together, or at least, not connect them to the internet.&lt;/p&gt;
&lt;p&gt;But it’s not only consumer appliances software that we can no longer trust.&lt;/p&gt;
&lt;p&gt;With raise of AI for coding, and so called “vibe-coders”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, we see an influx of software that was created either by engineers who have no knowledge in the domain they operate in, or software that simply lacks any review process that it is free from malicious code elements.
Moreover, said AI tools are not free from injecting malicious code, or simply running destructive operations, hence they require a process of sandboxing — isolating them from actual computers so if they make a mess, they ruin some virtual environment instead.
I have written about the need to isolate such AI tools on my tech blog: &lt;a class=&quot;internal new&quot; href=&quot;/articles/isolating-claude-code/&quot;&gt;Isolating Claude Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As software engineers, we rely on open-source software and libraries, that are built and maintained by other people and companies.
Any while these libraries are &lt;strong&gt;not&lt;/strong&gt; free from attempts to inject malicious code by malicious actors (see: &lt;a class=&quot;internal new&quot; href=&quot;/articles/xz-backdoor/&quot;&gt;xz backdoor&lt;/a&gt;, &lt;a href=&quot;https://helixguard.ai/blog/malicious-sha1hulud-2025-11-24/&quot;&gt;SHA1-HULUD injections&lt;/a&gt;, and various other &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;pypi&lt;/code&gt; attacks that happened recently), we are, &lt;em&gt;mostly&lt;/em&gt;, able to catch them in time.
The reasons, I believe, are because writing malicious code is hard, and in order to achieve desired effect you need to either target core libraries (as the case with &lt;code&gt;xz&lt;/code&gt;), or play the numbers game by infecting as many packages as possible (as the case with &lt;code&gt;SHA1-HULUD&lt;/code&gt;).
But, as the amount of code produced with AI is growing at a very fast rate, it is becoming hard to review software.
Many popular OSS libraries either suspended their bug bounty programs, or modified contribution guidelines, citing influx of AI generated code as the main reason&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.
Human fatigue is a real thing, and people tend to become sloppier as they deal with bigger workload.&lt;/p&gt;
&lt;p&gt;I guess what I’m trying to say is that if in the past it was somewhat safe to run arbitrary code downloaded from the internet (as long as it was downloaded from a reputable source), in today’s AI-era, everything needs to be sandboxed.
Every project you develop — needs to be run in an isolated VM that has no access to your machine and the secrets that you store there.
We use our computers for banking, and we tend to store credentials, which malicious code can extract.&lt;/p&gt;
&lt;p&gt;Household appliances, and other electronics devices, need to be either disconnected from the network, or run in an isolated VLAN networks that have no access to your main network.
I am sure that as we continue to navigate this new reality, new products will come up to serve for better security, but as for now, I conclude that &lt;strong&gt;we can no longer trust software&lt;/strong&gt;.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;For those who don’t know, Microsoft Copilot is Microsoft’s attempt to shove their AI in every place possible. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;Vibe-coder — a person who uses AI prompting to create software, often times knowing nothing about programming, or not having understanding of the problem’s domain in which they operate. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.theregister.com/2026/01/21/curl_ends_bug_bounty/&quot;&gt;curl shutters bug bounty program&lt;/a&gt;, &lt;a href=&quot;https://discourse.llvm.org/t/rfc-llvm-ai-tool-policy-human-in-the-loop/89159&quot;&gt;LLVM AI Tool Policy: human in the loop&lt;/a&gt;, &lt;a href=&quot;https://github.com/ghostty-org/ghostty/pull/8289&quot;&gt;Ghostty: AI tooling must be disclosed&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Isolating Claude Code</title><link>https://kudmitry.com/articles/isolating-claude-code/</link><guid isPermaLink="true">https://kudmitry.com/articles/isolating-claude-code/</guid><pubDate>Sat, 24 Jan 2026 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I have joined the vibe-coding club.
I used to laugh and vibe-coders who use Claude Code while accepting everything it asks for, until eventually CC decides to &lt;a href=&quot;https://www.reddit.com/r/ClaudeAI/comments/1pgxckk/claude_cli_deleted_my_entire_home_directory_wiped/&quot;&gt;delete their entire home directory&lt;/a&gt;, turning their computer into a brick.
But, life has a funny way of turning around and coming back at you.
Before this happens to me, I decided to take action.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Not today CC&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;888&quot; height=&quot;499&quot; src=&quot;/_astro/not-today-rm-rf.Bninxjem_ZYuR7i.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Not today CC&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Moreover, according to the creator of Claude Code, Boris Cherny, Calude Code writes Claude Code.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;This is how skynet started&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;1206&quot; src=&quot;/_astro/cc-writes-cc.7wocRRRO_ZSQLzI.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;This is how skynet started&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;And we all know that we can’t really trust AI.
So let’s talk about isolating CC.&lt;/p&gt;
&lt;h2 id=&quot;built-in-claude-code-sandboxing&quot;&gt;Built in Claude Code sandboxing&lt;/h2&gt;
&lt;p&gt;CC has built it sandboxing.
You can configure it with &lt;code&gt;/sandbox&lt;/code&gt; inside CC chat.
You can read about CC sandboxing &lt;a href=&quot;https://code.claude.com/docs/en/sandboxing&quot;&gt;here&lt;/a&gt;.
Long story shorty, Claude is good at escaping the sandbox.
Eventually, I would find myself glancing at the output of CC and seeing something long the lines of:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;permission denied to access db:5432&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[CC] sandboxing is on, trying without sandbox&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I might have accepted a permission here and there that allowed CC to escape its sandbox, so I don’t trust the built-in sandboxing.&lt;/p&gt;
&lt;h2 id=&quot;existing-tools&quot;&gt;Existing tools&lt;/h2&gt;
&lt;p&gt;The first option I was thinking about was: let’s see if an existing tool is available.
I asked Claude to tell me how I can isolate it’s brother Code, and Claude was glad to help.
It came up with 3 tools: &lt;code&gt;docker sandbox&lt;/code&gt;, &lt;code&gt;cco&lt;/code&gt;, and &lt;code&gt;claudebox&lt;/code&gt;.
I saw &lt;code&gt;claudebox&lt;/code&gt; mentioned on HackerNews the other day, so I though I might be onto something.&lt;/p&gt;
&lt;h3 id=&quot;how-i-work&quot;&gt;How I work&lt;/h3&gt;
&lt;p&gt;I have adopted a workflow where I run all my auxiliary services in docker compose.
Every app / service that I develop, has its own &lt;code&gt;docker-compose.dev.yml&lt;/code&gt; which brings all the needed services (database, redis, etc) up, so I can fully isolate versions, data, and accidentals fucks-ups.
Instead of installing all these tools on my host machine, trying to find workarounds for supporting different versions, I can just bring an entire stack for each app.
And when I’m done, I bring it down, and my machine is not wasting CPU cycles.
Or, if the project is no longer in development, I just nuke the entire docker container, with all its data, leaving my machine clean.&lt;/p&gt;
&lt;p&gt;This means that if I run CC inside a docker container, this docker container needs to be able to communicate with my dev environment.
I want CC to be able to run tests against the database, or even inspect the database directly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker sandbox&lt;/code&gt; does not support this.
You can &lt;code&gt;--mount-docker-socket&lt;/code&gt; which basically means that the docker where CC runs has root-privileged access to the Docker daemon, which kind-of beats the purpose of isolation.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cco&lt;/code&gt; (which stand for “Claude Condom”, funny name), has:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Network access&lt;/strong&gt;: Full host network access for localhost development servers, MCP servers, and web requests&lt;/p&gt;&lt;div class=&quot;flex justify-end&quot;&gt;&lt;a href=&quot;https://github.com/nikvdp/cco?tab=readme-ov-file#how-it-works&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;This means that if my host has access to a Docker container named &lt;code&gt;some-secret-app&lt;/code&gt; that CC should not access, &lt;code&gt;cco&lt;/code&gt; won’t protect me.&lt;/p&gt;
&lt;p&gt;Lastly, &lt;code&gt;claudebox&lt;/code&gt; does not support joining an existing Docker network, hence I wasn’t able to find a way to give &lt;code&gt;claudebox&lt;/code&gt; access to my auxiliary services.&lt;/p&gt;
&lt;h2 id=&quot;rolling-my-own&quot;&gt;Rolling my own&lt;/h2&gt;
&lt;p&gt;As an experienced software engineer, I know that the best practice is to always &lt;del&gt;use existing solutions&lt;/del&gt; roll your own solution.
I need CC to access my development Docker network, so the reasonable thing to do, was to put CC in the same docker compose file.
Create a Dockerfile for claude:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM ruby:3.4.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN apt-get update &amp;#x26;&amp;#x26; apt-get install -y \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    nodejs \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    npm \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    libvips-dev \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x26;&amp;#x26; rm -rf /var/lib/apt/lists/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN npm install -g @anthropic-ai/claude-code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WORKDIR /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CMD [&quot;bash&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I use ruby as the source image, since in this particular project it was for Ruby on Rails development.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, I added a service into my &lt;code&gt;docker-compose.dev.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  claude:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    build:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context: .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dockerfile: Dockerfile.claude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    stdin_open: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    tty: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    init: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    environment:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      CLAUDE_CONFIG_DIR: &quot;/claude&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      DATABASE_URL: &quot;postgresql://user:password@db:5432/db&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      REDIS_URL: &quot;redis://redis:6379/0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      TERM: xterm-256color&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    profiles:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - claude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    volumes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - .:/app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - ./.claude-credentials:/claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It took me some time to figure out how to persist authentication between container re-runs, and the key was to mount claude credentials, but also specify &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt;.
I also spent a lot of time trying to fix terminal issues, where key presses would not register, so it’s import to set &lt;code&gt;tty&lt;/code&gt;, &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;stdin_open&lt;/code&gt; and &lt;code&gt;TERM&lt;/code&gt;.
Finally, I created a bash script to run the docker container:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  continue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    docker&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; docker-compose.dev.yml&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --rm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; claude&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; script&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;claude --continue&quot;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; /dev/null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  *)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    docker&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; docker-compose.dev.yml&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --rm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; claude&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; script&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; claude&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; /dev/null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;esac&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;./claude.sh&lt;/code&gt; to start a new CC session, or &lt;code&gt;./claude.sh continue&lt;/code&gt; to resume an existing session.
Since the docker container is ad-hoc, and is removed after each run, this means you can run as many containers as you want.
This was it, and I &lt;em&gt;thought&lt;/em&gt; I was happy.&lt;/p&gt;
&lt;p&gt;But then, I started to question myself.
Docker container shares the kernel with the host machine, meaning CC could still bring down my entire computer.
Unlikely, but possible.
But more importantly, I was running the dev server of my application on the host machine, rather than in the container.
In general, I don’t like running my dev server in containers, since they overcomplicate stuff.
I know some people do it, but for me, pulling, and rebuilding the container kills the entire DX of rapid development.&lt;/p&gt;
&lt;p&gt;Since I was running the dev server on the host, and CC was editing the code in the container (which was a simple mount to the container), it is possible for CC to inject a code that would read an environment variable that was defined in my host machine, even though this variable is not available to the Docker container.
This malicious code can, for example, dump all the environment variables and send them to some obscure server.
Moreover, CC could still inject a variant of &lt;code&gt;rf -rf ~&lt;/code&gt; into the application code, which would still nuke my home directory, and turn my computer into a temporary brick.&lt;/p&gt;
&lt;p&gt;And so, to mitigate these two issues, I had to go back to the drawing board.&lt;/p&gt;
&lt;h2 id=&quot;rolling-my-own-with-blackjack-and-vms&quot;&gt;Rolling my own, with blackjack and VMs&lt;/h2&gt;
&lt;p&gt;Doing your own “CC isolation” framework is a very common project many people undertake these days.
And so, while browsing HackerNews, I came across a blog post by Emil Burzo: &lt;a href=&quot;https://blog.emilburzo.com/2026/01/running-claude-code-dangerously-safely/&quot;&gt;Running Claude Code dangerously (safely)&lt;/a&gt;.
And in this blog post, I saw something I haven’t seen for at least a decade: &lt;code&gt;Vagrant&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I remember using Vagrant back in 2010s, when I was playing with VMs, as well as toying with writing my own &lt;a href=&quot;https://github.com/skwee357/wrap-os&quot;&gt;Operating System Kernel&lt;/a&gt;.
Eventually, I switched to using &lt;code&gt;qemu&lt;/code&gt;, but nevertheless, seeing &lt;code&gt;Vagrant&lt;/code&gt; again has brought some warm memories.
And as Docker popularity peaked circa 2015-2016, Vagrant became less relevant, as container were faster, and lighter alternative.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Vagrant&lt;/code&gt; is basically “Virtual Machine as a code”.
Think &lt;code&gt;terraform&lt;/code&gt;, but for VMs.
You write a &lt;code&gt;Vagrantfile&lt;/code&gt; which describes your VM, and with commands like &lt;code&gt;vagrant up&lt;/code&gt; you bring the VM alive, and &lt;code&gt;vagrant ssh&lt;/code&gt; gives you &lt;code&gt;ssh&lt;/code&gt; access to the machine.
Unlike Docker containers, Vagrant VMs are fully isolated, and they run their own kernel, meaning that there is very little chance for malicious code to gain access to the host OS.
Vagrant support various VM providers, but the most popular one is VirtualBox from oracle.&lt;/p&gt;
&lt;p&gt;You define a VM by writing a &lt;code&gt;Vagrantfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vm_name&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;basename&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Dir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getwd&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NODE_VERSION&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vagrant&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;configure&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;box&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;bento/ubuntu-24.04&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;synced_folder&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/vagrant&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;virtualbox&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;SharedFoldersEnableSymlinksCreate&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;hostname&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; vm_name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;network&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;private_network&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;dhcp&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;provider&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;virtualbox&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;do&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;memory&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;4096&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cpus&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;gui&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; vm_name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;customize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;modifyvm&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;--audio&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    vb.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;customize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;modifyvm&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;--usb&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;off&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;provision&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;inline&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;#x3C;&amp;#x3C;-SHELL&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    export&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; DEBIAN_FRONTEND&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;noninteractive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    curl&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; https://deb.nodesource.com/setup_#{NODE_VERSION}.x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;bash&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; -&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    apt-get&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    apt-get&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -y&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; avahi-daemon&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        nodejs \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        git&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        unzip \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        curl&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        rustc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; command&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; docker&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x26;&gt; /dev/null; &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      curl&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; https://get.docker.com&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; @anthropic-ai/claude-code&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --no-audit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    usermod&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -aG&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; docker&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; vagrant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  SHELL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;provision&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;always&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;privileged&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;inline&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;#x3C;&amp;#x3C;-SHELL&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    cd&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; /vagrant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    bundle&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    docker&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; docker-compose.dev.yml&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; up&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  SHELL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like with Docker, I mount the working directory on the host machine to &lt;code&gt;/vagrant&lt;/code&gt; on the guest machine.
I have a provision script that will run the first time I create the VM.
The provision script is responsible for installing the needed dependencies (docker, NodeJS, and Ruby on Rails dependencies which I omitted from this code as they are specific to this particular project).
I have another provision script that always runs when I bring the VM up, which installs the needed ruby and node dependencies for my project, and start the development docker (postgres and redis in my case).&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;As of writing this blog post, it seems like the installation process for Claude Code has switched to “native install”.
Instead of running &lt;code&gt;npm i -g&lt;/code&gt;, you now need to run a shell script: &lt;code&gt;curl -fsSL https://claude.ai/install.sh | bash&lt;/code&gt; and add &lt;code&gt;~/.local/bin&lt;/code&gt; to your &lt;code&gt;$PATH&lt;/code&gt; like this &lt;code&gt;echo &apos;export PATH=&quot;$HOME/.local/bin:$PATH&quot;&apos; &gt;&gt; /home/vagrant/.bashrc&lt;/code&gt;
So make sure to replace the &lt;code&gt;npm&lt;/code&gt; line with these two lines.&lt;/p&gt;&lt;div class=&quot;flex justify-end&quot;&gt;&lt;a href=&quot;https://code.claude.com/docs/en/getting-started&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After the machine is up, I can ssh into it using &lt;code&gt;vagrant ssh&lt;/code&gt;, and start my app development server.
Then, I can open another ssh session, and start Claude Code.
I can have as many ssh sessions, and as many CCs as I need.
I can give CC full access to the VM, root permissions, whatever it needs, so it can install packages, run various tools, whatever.
It’s all inside the VM, and if the VM gets infected, I just destroy it, and bring a new one.&lt;/p&gt;
&lt;p&gt;Nothing app related runs on my host machine.
The only thing that breaks the isolation level is the code which is mounted to the VM.
For truly paranoid people, you can specify &lt;code&gt;type: &quot;rsync&quot;&lt;/code&gt;, in the &lt;code&gt;config.vm.synced_folder&lt;/code&gt; call, thus taking a &lt;strong&gt;copy&lt;/strong&gt; of the code into the VM.
In that case, changes made in the VM are not synced back to the host machine.
You either need to sync manually, or setup git flow from within the VM.
Some people like to vibe-code 100%, so they don’t edit code manually, but instead let CC make the changes and open PRs.
I still write code manually, and use CC for mundane tasks or code reviews, so I want to be able to edit the code myself, and control when and how I push to git, therefor I prefer to go with a synced folder approach even at the cost of penetrating the isolation level.&lt;/p&gt;
&lt;p&gt;Some of you might have noticed that I also install &lt;code&gt;avahi-daemon&lt;/code&gt; on the guest VM.
What I wanted to achieve is that I will be able to open a browser on my host machine, and access the code that is running in the guest VM.
This can be achieved by forwarding ports from the guest to the host, just like with docker, using the network configuration in &lt;code&gt;Vagrantfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;vm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;network&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;forwarded_port&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;guest&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 3000&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 3000&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;auto_correct&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But I don’t like to expose too many ports, as well as being able to remember what port does each service use.
If you run a PostgreSQL Docker in every VM, you eventually end up with port mappings like &lt;code&gt;54321&lt;/code&gt;, &lt;code&gt;54322&lt;/code&gt;, &lt;code&gt;54323&lt;/code&gt;, and so-on.
I wanted to be able to access the machine by it’s name.
It is possible to setup a static IP to the VM, and then access the services by the IP, for examples &lt;code&gt;192.168.10.100:5432&lt;/code&gt; for PostgreSQL, but then again, I’d have to memorize the IPs of every VM I run.
Another option is if you run a local DNS server (even PiHole or AdGuard Home), you can create a local DNS entry (or just edit &lt;code&gt;/etc/hosts&lt;/code&gt;) and map a name to an IP.
But, with mDNS you can do better.
By running &lt;code&gt;avahi&lt;/code&gt; on the guest machine, and properly setting the &lt;code&gt;hostname&lt;/code&gt; (using &lt;code&gt;config.vm.hostname = vm_name&lt;/code&gt;), you are able to “magically” access &lt;code&gt;http://vm_name.local&lt;/code&gt; from your host machine.
Just make sure to bind your services to &lt;code&gt;0.0.0.0&lt;/code&gt; instead of &lt;code&gt;127.0.0.1&lt;/code&gt; when you start them.&lt;/p&gt;
&lt;h2 id=&quot;why-you-probably-shouldnt-copy-my-approach&quot;&gt;Why you probably shouldn’t copy my approach&lt;/h2&gt;
&lt;p&gt;Someone recently asked on HackerNews, &lt;a href=&quot;https://news.ycombinator.com/item?id=46699324&quot;&gt;why so many people rolling out their own AI sandboxing solution&lt;/a&gt;, and I think the best answer was given by &lt;code&gt;varshith17&lt;/code&gt;, which basically boils down to:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Same reason everyone rolled their own auth in 2010, the problem is simple enough to DIY badly, complex enough that no standard fits everyone.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;My solution is tailored to my needs.
I run auxiliary services in docker containers, so I need a solution where CC can access these containers.
I don’t trust CC to access anything else, and I don’t trust myself to be able to correctly control permissions for CC after a long day of programming.
Eventually, I will slip and approve a request to &lt;code&gt;Bash(rm -rf some-old-code ~/)&lt;/code&gt;, hence rendering my machine unusable for some time.&lt;/p&gt;
&lt;p&gt;My solution does not cover things like proxying requests to make sure CC can only access certain websites, or safeguarding environment variables, as I use only dev credentials that can barely cause any harm if they are leaked.
If CC destroys my development database, I have a backup.
Your needs might be different, and you might find existing tools, like the ones I mentioned earlier, useful.
Or, you will decide to roll your own, and by having access to diverse information, and experience of others, you will craft a solution that suits your needs.&lt;/p&gt;
&lt;p&gt;Safe vibe-coding!&lt;/p&gt;</content:encoded></item><item><title>Dead Internet Theory</title><link>https://kudmitry.com/articles/dead-internet-theory/</link><guid isPermaLink="true">https://kudmitry.com/articles/dead-internet-theory/</guid><pubDate>Sun, 18 Jan 2026 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The other day I was browsing my one-and-only social network — which is not a social network, but I’m tired of arguing with people online about it — &lt;a href=&quot;https://news.ycombinator.com/&quot;&gt;HackerNews&lt;/a&gt;.
It’s like this dark corner of the internet, where anonymous tech-enthusiasts, scientists, entrepreneurs, and internet-trolls, like to lurk.
I like HackerNews.
It helps me stay up-to-date about recent tech news (like &lt;a href=&quot;https://news.ycombinator.com/item?id=46646645&quot;&gt;Cloudflare acquiring Astro&lt;/a&gt; which makes me happy for the Astro team, but also sad and worried since I really like Astro, and big-tech has a tendency to ruin things); it &lt;em&gt;mostly&lt;/em&gt; avoids politics; and it’s not a social network.&lt;/p&gt;
&lt;p&gt;And, in the fashion of HackerNews, I stumbled upon someone sharing their open-source project.
It’s great to see people work on their projects and decide to show them to the world.
I think people underestimate the fear of actually shipping stuff, which involves sharing it with the world.&lt;/p&gt;
&lt;p&gt;Upon glancing at the comment section, I started to see other anonymous participants questioning the validity of said open-source project in terms of how much of it was AI-generated.
I grabbed my popcorn, and started to follow this thread.
More accusations started to appear: the commit timeline does not make sense; the code has AI-generated comments; etc.
And at the same time, the author tried to reply to every comment claiming that they wrote this 100% without using AI.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I don’t mind people using AI to write code, even though I tried to resist it myself, until eventually succumbing to it.
But I think it’s fair to disclose the use of AI, especially in open-source software.
People on the internet are, mostly, anonymous, and it’s not always possible to verify the claims or expertise of particular individuals.&lt;/p&gt;&lt;p&gt;But as the amount of code is growing, considering that everyone is using AI to generate whatever-app they want, it’s impossible to verify every piece of code we are going to use.
So it’s fair to know, I think, if some project is AI generated and to what extent.
In the end, LLMs are just probabilistic next-token generators.
And while they are getting extremely good at most simple tasks, they have the potential to wreak havoc with harder problems or edge-cases (especially if there are no experienced engineers, with domain knowledge, to review the generated code).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As I was following this thread, I started to see a pattern: the comments of the author looked AI generated too:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The use of em-dashes, which on most keyboard require a special key-combination that most people don’t know, and while in markdown two dashes will render as em-dash, this is not true of HackerNews (hence, you often see &lt;code&gt;--&lt;/code&gt; in HackerNews comments, where the author is probably used to Markdown renderer turning it into em-dash)&lt;/li&gt;
&lt;li&gt;The notorious “you are absolutely right”, which no living human ever used before, at least not that I know of&lt;/li&gt;
&lt;li&gt;The other notorious “let me know if you want to [do that thing] or [explore this other thing]” at the end of the sentence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was sitting there, refreshing the page, seeing the author being confronted with use of AI in both their code and their comments, while the author claiming to have not used AI at all.
Honestly, I was thinking I was going insane.
Am I wrong to suspect them?
What if people DO USE em-dashes in real life?
What if English is not their native language and in their native language it’s fine to use phrases like “you are absolutely right”?
Is this even a real person?
Are the people who are commenting real?&lt;/p&gt;
&lt;p&gt;And then it hit me.
We have reached the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_Internet_theory&quot;&gt;Dead Internet&lt;/a&gt;.
The Dead Internet Theory claims that since around 2016 (a whooping 10 years already), the internet is mainly dead, i.e. most interactions are between bots, and most content is machine generated to either sell you stuff, or game the SEO game (in order to sell you stuff).&lt;/p&gt;
&lt;p&gt;I’m &lt;del&gt;ashamed&lt;/del&gt; proud to say that I spent a good portion of my teenage years on the internet, chatting and learning from real people who knew more than me.
Back in the early 2000s, there were barely bots on the internet.
The average non-tech human didn’t know anything about phpBB forums, and the weird people with pseudonyms who hanged-out in there.
I spent countless hours inside IRC channels, and on phpBB forums, learning things like network programming, OS-development, game-development, and of course web-development (which became my profession for almost two decades now).
I’m basically a graduate of the Internet University.
Back then, nobody had doubts that they were talking to a human-being.
Sure, you could think that you spoke to a hot girl, who in reality was a fat guy, but hey, at least they were real!&lt;/p&gt;
&lt;p&gt;But today, I no longer know what is real.
I saw a picture on LinkedIn, from a real tech company, posting about their “office vibes” and their happy employees.
And then I went to the comment section, and sure enough this picture is AI generated (mangled text that does not make sense, weird hand artifacts).
It was posted by an employee of the company, it showed other employees of said company, and it was altered with AI to showcase a different reality.
Hell, maybe the people on the picture do not even exist!&lt;/p&gt;
&lt;p&gt;And these are mild examples.
I don’t use social networks (and no, HackerNews is &lt;strong&gt;not&lt;/strong&gt; a social network), but I hear horror stories about AI generated content on Facebook, Xitter, TikTok, ranging from photos of giants that built the pyramids in Egypt, all the way to short videos of pretty girls saying that the EU is bad for Poland.&lt;/p&gt;
&lt;p&gt;I honestly got sad that day.
Hopeless, if I could say.
AI is easily available to the masses, which allow them to generate shitload of AI-slop.
People no longer need to write comments or code, they can just feed this to AI agents who will generate the next “you are absolutely right” masterpiece.&lt;/p&gt;
&lt;p&gt;I like technology.
I like software engineering, and the concept of the internet where people could share knowledge and create communities.
Were there malicious actors back then on the internet?
For sure.
But what I am seeing today, makes me question whether the future we are headed to is a future where technology is useful anymore.
Or, rather, it’s a future where bots talk with bots, and human knowledge just gets recycled and repackaged into “10 step to fix your [daily problem] you are having” for the sake of selling you more stuff.&lt;/p&gt;</content:encoded></item><item><title>Not knowing is part of the path</title><link>https://kudmitry.com/articles/not-knowing-is-part-of-the-path/</link><guid isPermaLink="true">https://kudmitry.com/articles/not-knowing-is-part-of-the-path/</guid><pubDate>Mon, 24 Nov 2025 21:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve noticed an interesting trend recently: everyone wants answers.&lt;/p&gt;
&lt;p&gt;No matter where you look, people just want to know the answer: to life, to business, to love, to friendship.
But the more I live, the more I tend to gravitate towards the idea that not having the answer is part of the path: and it’s the most important part.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I will frame the rest of the post from a view-point of a successful&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; business, but it can apply to anything else: successful life/love/friendship/etc.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I often see Reddit posts that read like: “&lt;em&gt;I now make $X per month, here what would I do if I started with $0 again&lt;/em&gt;”.
And sure, it sounds like a valuable piece of content.
Follow the steps, and you too, will make $X per month.
But there are two problems with this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The first problem&lt;/strong&gt; is people’s tendency to justify their success in a logical sense.
For some obscure reason beyond my understanding, people seem to be very repulsed towards the word &lt;strong&gt;luck&lt;/strong&gt;, despite that fact that luck plays a major role in success.
So most people who are successful, tend to find logical explanation to their success in a form of &lt;em&gt;morning rituals&lt;/em&gt;, &lt;em&gt;daily habits&lt;/em&gt;, and &lt;em&gt;reflection on past events&lt;/em&gt;.
&lt;em&gt;It’s because Bezos wakes up at 3AM, he is successful.&lt;/em&gt;
I see the same bucket list of &lt;em&gt;reflections on past events&lt;/em&gt;: build a product in market that has something missing; invest in marketing; etc.
It’s good all, but you also need shit tons of luck.
On the other hand, if you build a &lt;em&gt;framework&lt;/em&gt; for success, you can always find customers.
There is a reason why people who sell courses on Entrepreneurship, make most of their money from the courses and not their entrepreneurship endeavors (which in best case scenario are average SMBs&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and worst case scenario never made any money).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The second problem&lt;/strong&gt; is that even if one could come up with a bucket list of step-by-step instructions for building a successful business, the chance to replicate it is close to zero.
People are not machines, despite the fact that AI evangelists wants you to believe that we are, and hence they can replicate our brain with “the most likely next character in sequence” type of algorithm.
I digress.
People are unpredictable, they have cultural differences, moral values (or lack thereof), and world views (which nowadays mostly influenced by TikTok, rather than critical thinking).
And so when you see a “plan to succeed in business” and you try to replicate it, you will, inevitably, encounter humans (unless you found a way to sell to machines — in that case, kudos to you).
Hence, your success rate is mostly random.&lt;/p&gt;
&lt;p&gt;All this “guessing” game is equal to me having a good run in the casino, so I decide to come up with a “&lt;em&gt;Here are the top 5 things I did in order to break the bank: number 5 will shock you!&lt;/em&gt;” article.
I can go back and say that you need to &lt;del&gt;gamble&lt;/del&gt; invest your money in roulette, put everything on 13th 3 times in a row, then skip one session, drink a cocktail and pass out.
Because hey!
It worked for me!
Despite the fact that roulette has a 48% success rate no matter what you do.&lt;/p&gt;
&lt;p&gt;I’m not saying that luck is the only predicting factor for success.
Far from it.
Acquiring skills, and putting these skills to work is important as well, but it’s only when &lt;em&gt;preparation meets opportunity&lt;/em&gt; we usually achieve something, but you still need to prepare.
And as sad is it might sound, reading the “&lt;em&gt;5 steps to succeed in business&lt;/em&gt;” is not preparation — it’s entertainment.
I’d even say it’s not even entertainment, but a counterproductive and damaging activity.
It gives you a false feeling of skill and mastery, without doing the actual work.
It provides with the feeling of reward, without actually achieving anything.
And moreover, it makes you addictive to wanting to &lt;em&gt;know&lt;/em&gt; everything, therefor you keep reading more and more without actually doing the work, because no matter how much you read, you don’t know enough so you need to read more.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The more I know, the more I realize I know nothing.
— Socrates&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;But success usually lies in not knowing.&lt;/p&gt;
&lt;p&gt;Just like ads, reading these type of article about success is beneficial in one use-case: when you ask the right questions.
A book about marketing will be helpful when you try to do marketing and fail, and you ask the question of “how do I do marketing”?
It’s only then you should resort to reading these books and articles.
Mindlessly consuming “knowledge” does not move you forward.
But it’s only when you tried to move forward, but instead moved sideways, you need to consume “knowledge”.
And it all starts with a question.
You need to be curious and know what questions to ask.&lt;/p&gt;
&lt;p&gt;But asking questions is hard, it’s way easier to just read the “&lt;em&gt;5 things YOU need to do in order to make $X per month&lt;/em&gt;”.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Success here being what society deems to be success (fame, money, career, family, etc), rather than your internal feeling (i.e. one could justify that one is successful despite not having societal “approval” of success). &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;Big respect to small and medium business owners, you are the backbone of our societies. I’m just arguing that an SMB owner might not be the most qualified person to sell a course on “achieving your inner potential in life, love, business, and spiritually”. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>I&apos;m Netlify-Free!</title><link>https://kudmitry.com/articles/i-am-netlify-free/</link><guid isPermaLink="true">https://kudmitry.com/articles/i-am-netlify-free/</guid><pubDate>Tue, 23 Sep 2025 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every time someone asked on HackerNews/Reddit “what’s your blog setup”, I would reply “Well, I use Astro hosted on Netlify, &lt;strong&gt;but&lt;/strong&gt; I plan to move to a VPS”.
It has been going for more than a year.
More important things would be tackled first, and I kept promising the internet that I will move to my own VPS, &lt;em&gt;eventually&lt;/em&gt;.
Well, I’m glad to say that I finally did!
This website is not hosed on a VPS that I rent.
I still use Astro, and they are still statically generated, but I’m no longer in the hands of Netlify.&lt;/p&gt;
&lt;h2 id=&quot;what-difference-does-it-make&quot;&gt;What difference does it make?&lt;/h2&gt;
&lt;p&gt;The first question you might ask yourself: “What difference does it make?”
And it’s true, there is no specific reason to move away from Netlify.
It worked for me for a couple of years, and Netlify is a great tool.
But, like any tool that offers convenience, it comes at the expense of price.&lt;/p&gt;
&lt;p&gt;Sure, I never paid for Netlify since I was on their free tier.
But it’s a matter of one viral post until you hit the free tier limits (and I was close).
And then, it’s $9/mo, a bit more than 2 times the price of a VPS in Hetzner (not affiliated, just love their products).
I already have a couple of Hetzner servers, so I figured, why not?&lt;/p&gt;
&lt;p&gt;On top of that, “just for fun” as always a good excuse.
Netlify trades configuration for convenience.
Deploying Astro with Netlify is basically one-click.
But the moment you need another step in you CI/CD pipeline, or want to customize something, you are basically out of luck.&lt;/p&gt;
&lt;p&gt;So buckle up, and I will tell you how you too can free yourself from proprietary hosting providers.&lt;/p&gt;
&lt;h2 id=&quot;how-to-do-it&quot;&gt;How to do it?&lt;/h2&gt;
&lt;p&gt;You need 2 components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A web server / reverse proxy - I use &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt; because Caddy is the GOAT, with minimal config file, and SSL out of the box&lt;/li&gt;
&lt;li&gt;A CI/CD pipeline / runner - I use GitLab CI/CD, with a runner hosted on my hardware&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I run Caddy on bare metal, because reasons, but you can containerize it.
In my &lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;, which is the main caddy configuration file, I added this line &lt;code&gt;import /etc/caddy/sites/*.caddy&lt;/code&gt;, so that every file I put in &lt;code&gt;/etc/caddy/sites/&lt;/code&gt; will be included as part of caddy configuration.
This allows me to run both blogs, without messing with editing one config file, so everything is separate and nice.
&lt;a href=&quot;https://en.wikipedia.org/wiki/KISS_principle&quot;&gt;KISS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then comes the Caddyfile for each blog.
Very simple:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kudmitry.com {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    root * /var/www/kudmitry.com/current&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /assets/* Cache-Control &quot;public, max-age=31536000, immutable&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /_astro/* Cache-Control &quot;public, max-age=31536000, immutable&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        * Cache-Control &quot;public, max-age=3600&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    handle_errors {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        rewrite * /404.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        file_server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    file_server {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        precompressed br gzip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;www.kudmitry.com {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    redir https://kudmitry.com{uri}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Astro builds a new set of assets for each build, so it’s safe to cache &lt;code&gt;/assets/*&lt;/code&gt; and &lt;code&gt;/_astro/*&lt;/code&gt; forever.
As for the rest, I cache everything for 1 hour.
Error paths will be forwarded to &lt;code&gt;404.html&lt;/code&gt;, and everything is served precompressed with brotli or gzip.&lt;/p&gt;
&lt;p&gt;Finally, we need a &lt;code&gt;gitlab-ci.yml&lt;/code&gt; that describes our pipeline:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;variables&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  NODE_VERSION&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;24-alpine&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  SITE_NAME&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;kudmitry.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  DEPLOY_PATH&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/var/www/${SITE_NAME}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  DEPLOY_DIR&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;${DEPLOY_PATH}/${CI_COMMIT_SHORT_SHA}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  COMMIT_REF&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;${CI_COMMIT_SHORT_SHA}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;stages&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;deploy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;notify&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;.node&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  image&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;node:$NODE_VERSION&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  cache&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;$CI_COMMIT_REF_SLUG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    paths&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;.npm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  before_script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;npm ci --cache .npm --prefer-offline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;.ssh&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  image&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;alpine:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  before_script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;apk add --no-cache openssh-client rsync&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;eval $(ssh-agent -s)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;install -m 600 -D /dev/null ~/.ssh/id_rsa&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;echo &quot;$SSH_PRIVATE_KEY&quot; | base64 -d &gt; ~/.ssh/id_rsa&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;ssh-keyscan -H $SSH_REMOTE &gt;&gt; ~/.ssh/known_hosts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  after_script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;rm -rf ~/.ssh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  extends&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;.node&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  tags&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;npm run build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  artifacts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    paths&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;dist/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    expire_in&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;1 hour&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  only&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;www&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;deploy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  extends&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;.ssh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  dependencies&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;ssh ${SSH_REMOTE} &quot;mkdir -p ${DEPLOY_DIR}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;rsync -avz --delete dist/ ${SSH_REMOTE}:${DEPLOY_DIR}/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;ssh ${SSH_REMOTE} &quot;ln -sfn ${DEPLOY_DIR} ${DEPLOY_PATH}/current&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      ssh ${SSH_REMOTE} &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        cd ${DEPLOY_PATH}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        ls -td [0-9a-f]* | grep -v &quot;current&quot; | tail -n +4 | xargs rm -rf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  only&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  environment&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;production&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;https://kudmitry.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;caddy&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;deploy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  extends&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;.ssh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;rsync -avz Caddyfile ${SSH_REMOTE}:/etc/caddy/sites/${SITE_NAME}.caddy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;ssh ${SSH_REMOTE} &quot;caddy reload --config /etc/caddy/Caddyfile&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  rules&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;$CI_COMMIT_BRANCH == &quot;master&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      changes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Caddyfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;on_success&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;notify&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  image&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;alpine:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  when&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;on_success&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  before_script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;apk add curl&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      curl -s -F &quot;token=${PUSHOVER_API_TOKEN}&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              -F &quot;user=${PUSHOVER_USER_TOKEN}&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              -F &quot;message=Pipeline succeeded for $CI_PROJECT_NAME on branch $CI_COMMIT_BRANCH@$CI_COMMIT_SHORT_SHA&quot;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              https://api.pushover.net/1/messages.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;on_failure&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;notify&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  image&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;alpine:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  when&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;on_failure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  before_script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;apk add curl&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      curl -s -F &quot;token=${PUSHOVER_API_TOKEN}&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              -F &quot;user=${PUSHOVER_USER_TOKEN}&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              -F &quot;message=Pipeline failed for $CI_PROJECT_NAME on branch $CI_COMMIT_BRANCH@$CI_COMMIT_SHORT_SHA&quot;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              https://api.pushover.net/1/messages.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, I define all the needed variables.
Then, I have 3 stages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;build - builds the Astro code&lt;/li&gt;
&lt;li&gt;deploy - copy the Astro artifacts to the server, as well as the Caddyfile (if it was changed)&lt;/li&gt;
&lt;li&gt;notify - send a pushover notification to my phone that the deploy is done&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, I define 2 common blocks: &lt;code&gt;.node&lt;/code&gt; for NodeJS stages, and &lt;code&gt;.ssh&lt;/code&gt; for stages that need SSH.
&lt;code&gt;build&lt;/code&gt; builds the Astro code and stores the artifact.
&lt;code&gt;www&lt;/code&gt; copies the artifacts from &lt;code&gt;build&lt;/code&gt; stage over ssh to the remote server.
Here I also do an interesting trick (which is very old, and the first time I learned about it is years ago when I played with &lt;a href=&quot;https://capistranorb.com/&quot;&gt;Capistrano&lt;/a&gt;).
To avoid corrupted state, each deploy copies the files to &lt;code&gt;/var/www/kudmitry.com/${CI_COMMIT_SHORT_SHA}&lt;/code&gt;, where &lt;code&gt;$CI_COMMIT_SHORT_SHA&lt;/code&gt; is the first 8 characters of the git commit.
Once the files are copied, I use a soft-link named &lt;code&gt;current&lt;/code&gt; that points to this directory.
Caddy points to the &lt;code&gt;current&lt;/code&gt; directory, which always points to the lats commit.
Finally, I do some magic to keep the last 3 directories (in case I want to rollback or something like that).
This is how the directory tree looks:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; kudmitry.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; ├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 0c69eaa0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; ├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 4cdc8c1f&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; ├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 6ddf000d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; └──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; current&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; -&gt; &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;/var/www/kudmitry.com/0c69eaa0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence, switching between versions is a simple matter of changing where &lt;code&gt;current&lt;/code&gt; points to.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;caddy&lt;/code&gt; stage simply copies the &lt;code&gt;Caddyfile&lt;/code&gt; to &lt;code&gt;/etc/caddy/sites/${SITE_NAME}.caddy&lt;/code&gt;, and reload the caddy server to make sure it picks up the new configuration.&lt;/p&gt;
&lt;p&gt;Lastly, I have two stages, one that runs on success and one that runs on failure, that send me a pushover notification when the deploy is done.
Sure, I could have used one stage, that executes a script, which looks at some GitLab defined variable that tells me the state of the pipeline, and based on that constructs a message, but meh.
It works like this as well.&lt;/p&gt;
&lt;p&gt;That’s it.
That’s Netlify (more or less).&lt;/p&gt;
&lt;p&gt;Now sure, Netlify has more things like CDN, automatic forms, and serverless functions.
But I don’t use any of that.
Maybe if you do, then Netlify is for you.
I never had problems with them, so I guess they are cool.&lt;/p&gt;
&lt;p&gt;But in recent months I feel like the fun is being sucked out of being a software engineer, so doing small things like this, brings me a bit of joy in an otherwise, vast greyness of AI slopification.&lt;/p&gt;</content:encoded></item><item><title>Climbing La Om Peak in Romania</title><link>https://kudmitry.com/articles/climbing-la-om-peak-in-romania/</link><guid isPermaLink="true">https://kudmitry.com/articles/climbing-la-om-peak-in-romania/</guid><pubDate>Tue, 16 Sep 2025 18:00:01 GMT</pubDate><content:encoded>&lt;p&gt;During my &lt;a class=&quot;internal new&quot; href=&quot;/articles/5-days-in-romania/&quot;&gt;recent trip to Romania&lt;/a&gt;, I have decided to climb the La Om Peak.
There is very little information available in English for this climb, so I wanted to share my experience, and the mistakes I made, so you can prepare better.&lt;/p&gt;
&lt;h2 id=&quot;why-la-om-and-why-me&quot;&gt;Why La Om, and why me?&lt;/h2&gt;
&lt;p&gt;La Om is not the highest peak in Romania.
At 2,238m, it is 307m shy of Moldoveanu peak, which is the highest peak in Romania and sits at 2,545m.
And indeed, Moldoveanu was my first choice.
However, from the information I could gather, summiting Moldoveanu takes about 17 hours out-and-back, and so you either push really hard, or do an overnight stop.
The overnight stop is usually done at Cabana Podragu which is located at 2,136m.
But I booked my accommodations before I decided on which peak to climb, and I had no plans to bring a big hiking backpack, sleeping bag and mat, and cooking equipment, so Moldoveanu was not for me this time.&lt;/p&gt;
&lt;p&gt;Another popular peak is Omu.
Located in the Bucegi Mountains, Omu is 2,505m.
But Omu is very accessible.
There is a Cabana named Omu close to the peak, and the nearby city of Busteni has a cable car that brings you to about 2,100m, making Omu peak very touristic.
I, however, wanted something more secluded.
So, La Om it is.
Moreover, the descriptions of La Om caught my curiosity: an exposed limestone ridge, shaped like a saw and regarded as one of the most beautiful sites of the entire Carpathian Mountains.&lt;/p&gt;
&lt;p&gt;I have some experience with climbing peaks in the past.
The first peak I ever did was Batur Volcano in Bali, which sits at 1,717m.
In New Zealand, I climbed mount Robert which sits at a shy 1,421m.
And relatively recently, I climbed &lt;a class=&quot;internal new&quot; href=&quot;/articles/hiking-acatenango/&quot;&gt;Volcano Acatenango&lt;/a&gt; in Guatemala, which according to my Apple Watch was at 3,612m.
Before that, I climbed Pacaya in Guatemala, reaching to about 2,200m.
So I’m not entirely new to climbing mountains, but I was not prepared for what was to come…&lt;/p&gt;
&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;/h2&gt;
&lt;p&gt;The initial plan was simple.
I was staying in Brasov, and I’m a sucker for hotel breakfasts, so I intended to wake up at 6:45, arrive at hotel breakfast by 7:00, and by 8:00 be ready to leave to Cabana Plaiul Foii which is the trailhead.
The drive takes about 50 minutes, so I planned to start my hike at around 9:00-9:10.
I had two days reserved for the hike, in case of bad weather: Friday the 5th of September, and Saturday the 6th.
Both days had great weather, so I did the hike on Friday.&lt;/p&gt;
&lt;div data-leaflet=&quot;&quot; data-gpx=&quot;/tracks/2025/ro/laom-loop.gpx&quot; data-stats=&quot;distance,elevation,altitude&quot;&gt;&lt;/div&gt;
&lt;p&gt;I was planning to do a loop from Cabana Plaiul Foii, which goes to Refugiul Șpirlea, up to Vf. La Om.
Then I’d go on the exposed ridge towards Refugiul Ascuțit, and descend towards Refugiul Sperantelor and back to the Cabana.
A moderate 15km track that according to OsmAnd would take about 10 hours.
It was way too optimistic in both the distance and the time.&lt;/p&gt;
&lt;h2 id=&quot;the-actual-hike&quot;&gt;The actual hike&lt;/h2&gt;
&lt;h3 id=&quot;cabana-plaiul-foii-to-la-zaplaz&quot;&gt;Cabana Plaiul Foii to La Zaplaz&lt;/h3&gt;
&lt;p&gt;The first part of the hike was very enjoyable.
It starts in an open field, and follows the river into the forest.
Piatra Craiului National Park, like the rest of Romania, is a home of brown bears, so it’s important to have a bear spray.
For the entire hike up to La Zaplaz — I was alone on the track.
Hiking alone in bear territory is not recommended, but it is what it is.
Make sure to make some noise (speak out loud, clap your hands) to let the bears know that you are there, avoid eating while hiking, and make sure your food does not smell.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;View of Piatra Craiului Mountains from Cabana Paliul Foii&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/piatra-mountains-from-cabana.DL5waAZR_RhyT5.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;View of Piatra Craiului Mountains from Cabana Paliul Foii&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I started my hike at 9:20, and by 10:45 I was at Spirla Shelter.
The signs said it will take between 2 and 2 and half hours to get to Spirla, so I was moving faster.
Good start.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;15 minutes before reaching Spirla&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/forest.l-Txbghl_gyv5P.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;15 minutes before reaching Spirla&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;At Spirla I did my first 15-minute break, had some food and rest, and moved on.
From Spirla it takes between 3-3.5 hours until you reach the peak.
There is a short section of about 20–30 minutes of forest, after which you reach the exposed limestone mountain which looks like it’s 90 degrees, and you have no idea how you are going to climb it.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Exposed limestone edge. How the hell am I going to climb it?&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/limestone-edge.BRADzKPO_Z23Xi5c.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Exposed limestone edge. How the hell am I going to climb it?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;la-zaplaz-and-la-lanțuri&quot;&gt;La Zaplaz and La Lanțuri&lt;/h3&gt;
&lt;p&gt;After leaving the forest, you will enter the technical part of the track.
For the next 2 to 3 hours, you will scramble, walk on scree, and use secured cables to reach the peak.&lt;/p&gt;
&lt;p&gt;I was not prepared for this.
This was nothing like my previous hikes.
In my previous hikes, I would mainly gain elevation by walking up; here I had to scramble, often with cables for assistance.
I never used cables for assistance before, and I did not have a helmet or gloves.
I tried to research before to make sure there are no cables involved, but failed to find any credible information.
It’s a bit ironic because this part of the track is called La Lanțuri which literally translates as “At the Chains” from Romanian.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Climbing using cable for assistance&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/edge-and-cables.6b-ZdZf4_Z18Y4uv.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Climbing using cable for assistance&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;More cables&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/more-climbing.CG-DadWF_TnI7I.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;More cables&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;And some scrambling&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/scrambling.Bv9LYpFm_fNFKv.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;And some scrambling&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;My focus was sharp, I was moving very carefully, slowly gaining elevation as I was approaching the peak.
I met two people who were going down, and closer to the top I met another solo hiker who was also going down.
Every section I conquered, I looked back down to appreciate the beauty of this mountain.&lt;/p&gt;
&lt;p&gt;The weather was beautiful, sunny and warm.
I was climbing from the west side of the ridge, meaning I was mostly in the shade.
About 40–50 minutes before reaching the peak, the sun was in zenith, and I stared to feel the heat.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Closer to the peak&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/closer-to-the-peak.hACgfRNs_27lj5t.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Closer to the peak&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;And then, after about 3 hours and 30 minutes of climbing, I have reached Șaua Grindului — a shelter at the ridge about 15 minutes from the peak.
From there I saw La Om peak.
A quick 15 minutes hike, and I was at the peak at 14:59.&lt;/p&gt;
&lt;h3 id=&quot;vf-la-om&quot;&gt;Vf. La Om&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Me at the peak: Vârful La Om, Piscul Basciului, 2,238m&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/laom-peak.BkYAFgT8_12CEij.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Me at the peak: Vârful La Om, Piscul Basciului, 2,238m&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;At the peak, I took a well deserved 30 minutes break.
I ate my cheat meal of a Milka chocolate bar, together with some tortillas, cascaval cheese and pastrami.
A glance at my phone revealed that I was at about 8km in distance.
With the sunset at 19:57, I had about 5 hours to get down in order to avoid walking in the darkness in the forest.
After some rest, pictures, and chat with other hikers — at 15:30 I started to move again towards my next point: Ascuțit.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;View from the peak towards Bucegi Mountains&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/view-from-peak.D-gJn03s_4ysYy.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;View from the peak towards Bucegi Mountains&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The signs said it will take between 2 and 2.5 hours, but I &lt;em&gt;assumed&lt;/em&gt; it will be faster — in the end, it’s mostly flat walk on the ridge.
It turned out to be anything but flat.
You traverse the ridge up and down all the time, on a narrow path and in some places you have a foot length of space.
I knew about this part, and it’s indeed mentally challenging, especially if you have fear of heights, which I don’t, so I was moving at a good pace (or so I thought).
As I was walking for about an hour and a half, I have reached a point where I saw the markings of the trail, but had no idea where to go next.
My strength was fading out, and I started to lose my focus.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Walk on the ridge&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/walk-on-the-ridge.BKkGLygi_ZFYsPV.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Walk on the ridge&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I have opened my map to check on my progress, assuming I’m almost at Ascuțit, but I was barely half way.
And then I sat and decided to reassess my situation.
The time was about 17:00.
I had at least one and a half hours until Ascuțit.
And who knows what’s afterward?
Is the way down as challenging as the way up?
Although based on the topography, the ridge looked less wide than the one I did during the climb.
This could mean a steeper slope with potentially harder scrambling and cables.
Nevertheless, I had a choice to make: push forward into an unknown path, risking getting stuck in the darkness; or go back the way I came.
Sure, I could get caught by the darkness, but at least I know the way, I walked it a few hours ago.&lt;/p&gt;
&lt;p&gt;I have decided to trust my gut feeling and turn back.&lt;/p&gt;
&lt;p&gt;It took me 50 minutes to get back to La Om.
As I was approaching the peak, I saw a person there.
I was never so happy to see a stranger in my life, hoping that maybe they are like me, going down, and so we could go together.&lt;/p&gt;
&lt;p&gt;When I reached the peak, I spoke with the guy, and he told me he plans to stay with his two friends, who are still behind, in Ascuțit shelter for the night.
Realizing that I will have to go down by myself, I wished them luck and went on.
As I reached the beginning of the way down, I saw a sign: “Spirla 4h”, “Cabana Plaiul Foii 5-6h”.&lt;/p&gt;
&lt;p&gt;I didn’t have 6 hours, let alone 4.
I &lt;strong&gt;had&lt;/strong&gt; to be at the forest before dawn.
There is no way I could descend with a headlamp.
Being in a forest with bears and headlamp could be manageable, but if I get stuck on the edge when the sun is down…&lt;/p&gt;
&lt;p&gt;There is cell reception at the peak, and so I sent my wife — who knew my entire route and the time estimates (as well as how to contact rescue services) — that I’m preparing to go down, and it should take me about 3 hours, so 4 with a buffer.
I made sure the message was delivered, and at 18:10 — with an hour and a half of sunlight left — I started to make my way down.&lt;/p&gt;
&lt;h3 id=&quot;the-way-down&quot;&gt;The way down&lt;/h3&gt;
&lt;p&gt;I guess at that point my body was running on adrenaline.
I took 3 liters of water with me, but at that moment I had less than a liter left, so I tried to conserve as much as I could.
The way down was relatively smooth.
I was using a technique where I would sit on my butt, make sure my hands are firm, find a contact point with one of my legs, and put the other one down.
In some places I was doing a little slide on the scree while sitting.
Not sure if this is the “proper” way to descend, but it worked for me.&lt;/p&gt;
&lt;p&gt;I made one mistake when going down, where I could not safely reach a point of contact with my feet, so I had to do a little jump, maybe half a meter.
At the moment I didn’t feel any pain, but as of publishing this I still have a slight pain in a muscle around my knee.
Hopefully it will go away.&lt;/p&gt;
&lt;p&gt;I saw a chamois.
It was standing there, looking at me.
So beautiful.
My phone was running out of battery, so I put it in my backpack connected to a power bank, and I didn’t take a picture.&lt;/p&gt;
&lt;p&gt;I also met a family (I believe) of 3 on their way up.
They didn’t speak good English.
We wished good luck to each other, and they were the last humans I would see for the next 2 and a half hours as I descend into darkness.&lt;/p&gt;
&lt;p&gt;I don’t remember the exact time the sunlight went out, but I had my headlamp prepared about 40 minutes before sunset.
As I was in the mountains, going down into a valley and a dense forest, I wasn’t hoping for twilight.
When the sun went down, everything went dark.
Luckily, by that time I was off the edge and the scree, making my entrance into the forest.&lt;/p&gt;
&lt;p&gt;By that point I ran out of water.
As I no longer needed my hands for support, I took out my bear spray, removed the safety pin, and held it my hand for the rest of the hike.&lt;/p&gt;
&lt;p&gt;I had a mental model of the path in my head.
I knew that by the time I reach Spirla, there would be about an hour left.
After Spirla, you walk along the river.
I also remembered that there would be a shallow and narrow river crossing about 10 minutes before you exist the dense forest, and so these were my marks.
I tried not to look at my phone too much, as the bright screen would blind me in complete darkness.
My only mission was to move from one trail marker to the next.&lt;/p&gt;
&lt;p&gt;At 20:21 I reached Spirla in complete darkness.
I was considering to do a quick snack break, as I had an apple which could provide the needed water, but I decided to move on.
There was a cell reception zone around Spirla, so I wrote another messages to my wife: “one hour to go, let’s take one and half”.&lt;/p&gt;
&lt;p&gt;After Spirla the road was mostly flat, and I was moving fast, like really fast.
My average speed was around 5km/h, which considering I was going in a forest, in complete darkness (aside for the light from my headlamp), is not bad at all.
As I was moving, I was making a noise every 15 seconds to let any bears know that I’m there.
Every odd noise, every shadow cast by my headlamp — would make my heart race more and more.&lt;/p&gt;
&lt;p&gt;And then, at around 21:05 I have exited the forest, and at 21:23 I was by my car near Cabana Plaiul Foii.
I had another bottle of water in my car which I drank until empty.
After an hour of driving, I arrived back at my hotel in Brasov.&lt;/p&gt;
&lt;p&gt;This concludes the entire hike which lasted for 12 hours and 3 minutes.
Below you can find the GPX for my hike.
Keep in mind that elevation gain/loss is inaccurate, and in reality, should be close to 1,700m.&lt;/p&gt;
&lt;div data-leaflet=&quot;&quot; data-gpx=&quot;/tracks/2025/ro/laom-recorded.gpx&quot; data-stats=&quot;distance,duration,altitude,elevation&quot;&gt;&lt;/div&gt;
&lt;h2 id=&quot;aftermath-and-tips&quot;&gt;Aftermath and tips&lt;/h2&gt;
&lt;p&gt;As I said I have experience climbing peaks and gaining a lot of elevation.
This hike was difficult physically, but not challenging.
I think it was way more challenging psychologically and mentally.
Knowing that at some places, if I misstep I could fall down, demanded a lot of mental energy and moving slowly and carefully.&lt;/p&gt;
&lt;p&gt;Every place I read recommended taking at least 3L of water, and I did, but I ran out of it.
Maybe because the weather was really sunny.
In case you plan to do this ascend, I recommend taking 4L of water.&lt;/p&gt;
&lt;p&gt;I also had all the layers in terms of clothing, but didn’t really need them.
A fully charged powerbank and a good headlamp are a must.
I would also take helmet and gloves for the scrambling part.&lt;/p&gt;
&lt;p&gt;I believe it was stupid to rush the way down and navigate the forest during darkness, but I had no other option.
The decision to turn back was correct, there is something in the brain which allows you to navigate easier, and be less stressed, when you already know the path.
Ideally, I would spend the night at Șaua Grindului near the peak, or worst case scenario at Spirla, but I did not have a sleeping mat and bag, and had no water.
I planned to go up and down the same day.&lt;/p&gt;
&lt;p&gt;Doing it solo is also more dangerous, so I would recommend doing it with another person, and definitely leave your plans with someone else.
There is cell reception at the peak, and at some places on the route, but most of the route has no cell reception.
I did it on Friday, and as I mentioned, there wasn’t that many people.
I saw 3 people when I was going up, about 5 people at the peak, and 3 more on my way down.
Maybe on weekends there is more traffic.&lt;/p&gt;
&lt;p&gt;Lastly, I would start early.
It’s better to stay at Cabana Plaiul Foii (or any other Cabana nearby), instead of driving from Brasov.
The sun rises at around 7AM, so you get almost 13 hours of sunlight (at least in early September), and it’s better to use it in case of emergency like running out of strength or stamina.
Needless to say that you should &lt;strong&gt;not&lt;/strong&gt; attempt to ascend in bad weather.
With rain, the scramble will be slippery, and with fog, you will put yourself in danger on the ridge as it is very narrow.
Monitor weather, and allow for alternative days.
I had 2 days planned for the hike, Friday and Saturday, but Friday turned out to be very sunny, so I used Saturday to relax.&lt;/p&gt;
&lt;h2 id=&quot;closing-notes&quot;&gt;Closing notes&lt;/h2&gt;
&lt;p&gt;This was definitely one of the hardest things mentally and psychologically that I ever did.
I did some stupid mistakes like climbing without proper gear and navigating a bear territory in complete darkness, but I was careful when climbing up and down, and had a headlamp and bear spray on my way in the forest.&lt;/p&gt;
&lt;p&gt;I’m kind of happy that there weren’t that many people, as I had the opportunity to enjoy solitude while climbing this magnificent mountain.
Being there by myself allowed me to reflect on a lot of things that are currently happening in my life.&lt;/p&gt;
&lt;p&gt;After coming back, I was reading some more about La Om, and from the information I could gather, La Om ascent from La Lanțuri is considered one of the hardest peaks in Romania.
And so now I have an achievement and an experience that allowed me to push my limits and better understand and trust myself.
I also learned a lot about the way we, humans, operate in stressful environment.
It’s important to trust your gut, despite the fact that you might feel like you want to push it.
I’m a bit upset that I wasn’t able to finish the full loop, but at the same time I was confident in my decision to turn back the way I came.
I struggled recently with understanding the difference between fear and gut feeling, and at the moment when I took out my phone and realized I might not make it, I strongly felt my gut telling me to go back.&lt;/p&gt;
&lt;p&gt;Overall, I think it was one of the unique hikes / climbs I ever did.
If you decide to do it, just make sure to have enough water, enough time, and proper gear.&lt;/p&gt;</content:encoded></item><item><title>5 days in Romania</title><link>https://kudmitry.com/articles/5-days-in-romania/</link><guid isPermaLink="true">https://kudmitry.com/articles/5-days-in-romania/</guid><pubDate>Tue, 16 Sep 2025 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I went on an extended weekend in Romania.
I’d like to share some of the activities I did there.
It’s important to note that I no longer travel for the cities, and look more towards beautiful nature.
Being located in the Netherlands, you get tired of the flatness, and so the purpose of the trip was to get to a country farther away — I’m kind of tired of Germany and France — with stunning nature and mountains.
Romania delivered in that regard.&lt;/p&gt;
&lt;h2 id=&quot;one-bag&quot;&gt;One bag&lt;/h2&gt;
&lt;p&gt;It’s not the first time I travel with one bag.
I have learned about one-bagging from the &lt;a href=&quot;https://reddit.com/r/onebag&quot;&gt;/r/onebag&lt;/a&gt; Reddit community long time ago, and as courtesy to them, I’d like to start by sharing my one-bag setup.&lt;/p&gt;
&lt;h3 id=&quot;electronics&quot;&gt;Electronics&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Electronics&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/electronics.B7c55gDp_Z1dF84L.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Electronics&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;USB-C to USB-C 100W cable (usually for my laptop which I didn’t take, but it always stays in my electronics bag)&lt;/li&gt;
&lt;li&gt;USB-A to USB-B charging cable (for kindle and powerbank)&lt;/li&gt;
&lt;li&gt;USB-C to Lighting cable (for my iPhone and Airpods)&lt;/li&gt;
&lt;li&gt;UGreen USB-C to USB-A adapter (sometimes cars have USB-A port for CarPlay / Charging, so instead of carrying two lighting cables, I carry this adapter)&lt;/li&gt;
&lt;li&gt;UGreen 100W charger&lt;/li&gt;
&lt;li&gt;Tomtoc electronic bag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In terms of electronics, I don’t carry much, only the 3 needed cables and a charger.&lt;/p&gt;
&lt;h3 id=&quot;miscellaneous&quot;&gt;Miscellaneous&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Miscellaneous&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/misc.R2jYjCKG_Z1cLlQv.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Miscellaneous&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Sea to Summit dry bag&lt;/li&gt;
&lt;li&gt;BlackDiamond Spot-R rechargeable headlamp&lt;/li&gt;
&lt;li&gt;Labello lip balm&lt;/li&gt;
&lt;li&gt;Casio G-Shock GW-M5610U-1&lt;/li&gt;
&lt;li&gt;Sun glasses&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.suprgood.com/&quot;&gt;Supr Good&lt;/a&gt; wallet&lt;/li&gt;
&lt;li&gt;Anker PowerBank&lt;/li&gt;
&lt;li&gt;Apple Airpods&lt;/li&gt;
&lt;li&gt;Two Field Books&lt;/li&gt;
&lt;li&gt;A pen&lt;/li&gt;
&lt;li&gt;Passport&lt;/li&gt;
&lt;li&gt;Kindle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As most of my trip was around nature, there are some hiking related items.&lt;/p&gt;
&lt;h3 id=&quot;first-aid-kit&quot;&gt;First aid kit&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;First aid kit&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/firstaid.U_XmlQ80_2kI9FQ.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;First aid kit&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Deuter first aid kit bag&lt;/li&gt;
&lt;li&gt;Sterile pads&lt;/li&gt;
&lt;li&gt;Tooth picks&lt;/li&gt;
&lt;li&gt;Tick removal tool&lt;/li&gt;
&lt;li&gt;Disinfectant&lt;/li&gt;
&lt;li&gt;Painkillers&lt;/li&gt;
&lt;li&gt;Gloves&lt;/li&gt;
&lt;li&gt;Triangular bandage&lt;/li&gt;
&lt;li&gt;Big and small bandages&lt;/li&gt;
&lt;li&gt;Bandage scissors&lt;/li&gt;
&lt;li&gt;Band-aid in a roll&lt;/li&gt;
&lt;li&gt;Assortment of band-aids&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing much to explain.
I usually take this first aid kit every time I travel.&lt;/p&gt;
&lt;p&gt;Fun story: just before my big hike, I accidentally dropped the first aid kit onto a bridge.
It bumped off the bridge, and fell into the river below.
The river was shallow, so I was able to fish the first aid kit, but most of the stuff in it became wet, so I had to throw the contents away and replace with a new kit I got from Decathlon.&lt;/p&gt;
&lt;h3 id=&quot;dopp-kit&quot;&gt;Dopp kit&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Dopp kit&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/doppkit.3MIZESJT_OaTmn.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Dopp kit&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.graveltravel.com/products/explorer-mini&quot;&gt;Gravel Explorer Mini&lt;/a&gt; toiletry bag&lt;/li&gt;
&lt;li&gt;Two travel sized Sensodyne toothpastes (one was almost empty)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chicagocomb.com/collections/pocket-and-travel/products/model-no-2-carbon-fiber-comb-daily-use-pocket-travel-and-beard-comb-for-fine-and-thinner-hair-ultra-smooth-anti-static-made-in-usa&quot;&gt;Chicago Comb&lt;/a&gt; Carbon Fiber Model 2 travel comb&lt;/li&gt;
&lt;li&gt;Wild deodorant&lt;/li&gt;
&lt;li&gt;Hand cream&lt;/li&gt;
&lt;li&gt;SPF cream&lt;/li&gt;
&lt;li&gt;Jordan toothbrush&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;underwear-and-socks&quot;&gt;Underwear and socks&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Underwear and socks&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/underwear.9f3h84yO_ZqQfDz.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Underwear and socks&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;6 pairs (5 + 1 extra) Calvin Klein underwear&lt;/li&gt;
&lt;li&gt;5 pairs of Levis socks (4 black, 1 white)&lt;/li&gt;
&lt;li&gt;1 pair of SmartWool socks (for hiking)&lt;/li&gt;
&lt;li&gt;eBags small packing cube&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;warm-clothes&quot;&gt;Warm clothes&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Warm clothes&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/warm.D3rQLwFz_10kl6M.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Warm clothes&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Patagonia button up shirt&lt;/li&gt;
&lt;li&gt;Patagonia nano-puff coat&lt;/li&gt;
&lt;li&gt;The North Face wind and rain coat&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;packed-clothes&quot;&gt;Packed clothes&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Packed clothes&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/cloth.fFIknPRA_Z1QbENa.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Packed clothes&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Amazon Basics Sun Hoodie (blue)&lt;/li&gt;
&lt;li&gt;Patagonia short sleeve t-shirt (gray)&lt;/li&gt;
&lt;li&gt;Patagonia long sleeve t-shirt (gray)&lt;/li&gt;
&lt;li&gt;Sun buff&lt;/li&gt;
&lt;li&gt;The North Face hiking pants&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;clothes-i-wear-on-the-flight-day&quot;&gt;Clothes I wear on the flight day&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Clothes I wear on the flight&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/wearing.BRuVvX1j_Z2jgEyU.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Clothes I wear on the flight&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Uniqlo pants&lt;/li&gt;
&lt;li&gt;Bamboo black t-shirt&lt;/li&gt;
&lt;li&gt;Levis white socks&lt;/li&gt;
&lt;li&gt;Calvin Klein underwear&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;shoes&quot;&gt;Shoes&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Shoes&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/shoes.BvfbryiN_21y3Ec.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Shoes&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Vivo Barefoot Primus Trail hiking shoes (packed for the flight)&lt;/li&gt;
&lt;li&gt;Vivo Barefoot Primus Lite Knit Natural sneakers (worn during the flight)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;everything-laid-out-and-packed&quot;&gt;Everything laid out and packed&lt;/h3&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Everything laid out&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/everything.DBzjADTk_98ucK.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Everything laid out&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The items that were not pictured before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buff hat&lt;/li&gt;
&lt;li&gt;Osprey Day Lite Plus hiking backpack (it was packed inside the main backpack, to be used for day hikes)&lt;/li&gt;
&lt;li&gt;A couple of garbage bags (leave no trace)&lt;/li&gt;
&lt;li&gt;KleanKanteen water bottle&lt;/li&gt;
&lt;li&gt;&lt;a class=&quot;internal new&quot; href=&quot;/articles/minaal-carry-on-3-0-review/&quot;&gt;Minaal Carry-On 3.0 backpack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Everything packed&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/packed.MAveuMIE_Z1P6H63.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Everything packed&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;/h2&gt;
&lt;h3 id=&quot;day-one---car-pickup-and-drive-to-brasov&quot;&gt;Day one - car pickup and drive to Brasov&lt;/h3&gt;
&lt;p&gt;Thursday, 4th of September, I catch an early 9AM flight from Amsterdam to Bucharest.
I grab some breakfast at the airport, and board on a 2.5 hours flight.
Upon arrival at 2PM, I pick up my rental car from Sixt.&lt;/p&gt;
&lt;p&gt;I always get nervous when I rent cars.
There is always a potential to get screwed: a scratch that you missed, a deposit that was not released, or sleazy sales people.
I usually rent with Sixt, as I had mostly good experience with them.
I rented a few cars from Sixt in the Netherlands, twice in Germany, once in Croatia (where I had the worst experience), and now in Romania.
Otopeni airport is awful, in addition there is big construction going on.
Sixt rental office is about 4-minute drive / 15-minute walk from the airport, but there should be a shuttle.
Since I was not able to find the shuttle, I decided to walk to the rental office.
Upon arrival, a Sixt employee told me that I needed to check in at the desk in the airport 🤦‍♂️.
The shuttle took me back to the airport, where I checked in, and then brought me back to the pickup point.&lt;/p&gt;
&lt;p&gt;And, my spidey senses of getting screwed worked correctly.
When I paid for the car, I used Revolut virtual card.
Upon checking in for the car, I was requested to leave a deposit using the same card, presented &lt;strong&gt;physically&lt;/strong&gt;.
I tried to explain that I don’t have this card physically, only in Apple Wallet, to which the employee told me they require a physical card and can’t charge the deposit on the virtual card.
I offered another credit card, physical, in my name, for the deposit, but the employee declined my offer, citing the contract that I need to arrive with the same card I made the booking with.&lt;/p&gt;
&lt;p&gt;We came to an agreement to cancel and fully refund my order, and place an immediate order for the same car with the same conditions, except for… the price.
I would have to pay the price of today, rather than what I paid when I made the order.
Bummer.
I tried to argue with him, but it didn’t really work, and I needed a car, so I ate the €90 price increase, and got my car.&lt;/p&gt;
&lt;p&gt;As of today, my deposit was released, my original payment was fully refunded, so so-far everything is good.
Aside from me not reading the contract, the experience was fine this time.
&lt;strong&gt;Tip&lt;/strong&gt;: don’t use virtual card for car reservations.&lt;/p&gt;
&lt;p&gt;I also got freaked out at the end of my trip because Romania has a system of toll roads for which you need a vignette.
A vignette costs a few euros for a few days, and I &lt;strong&gt;knew&lt;/strong&gt; that, but somehow forgot about it.
After reading, I realized that the fines could be up to €100 per violation, and I drove at least on 3 roads that were classified as toll roads.
I was able to, eventually, find a government website where you can input the license plate + the VIN number to check whether the car has an active vignette, and it did.
I also asked the Sixt representative at the end of my rental whether the car has a vignette for toll roads, and he told me that it does.
But always remember to ask your rental company, and don’t rely on my words, whether the car you rent has a vignette.&lt;/p&gt;
&lt;p&gt;After picking up the car, I headed straight to Brasov — a city in the Carpathian Mountains — which would become my home base for nature trips.
The drive from Bucharest to Brasov is about 3 hours, but since it’s a mountain road, expect delays.
I planned to stop in a town called Sinaia, where you can view the Peleș Castle (no, it’s not the Dracula Castle), which is on the way, and have some lunch there.
When I arrived in Sinaia, there were road closures and congestions, and I needed to arrive in Brasov before 8 PM since I had to buy a bear spray.
Because of the time I lost in traffic, I decided to skip Peleș Castle, with a hope to stop by on my way back.&lt;/p&gt;
&lt;p&gt;I arrived in Brasov at 18:00, checked in at my hotel, and wanted to take a shower and walk to the city center for late lunch / dinner, as well as grab my bear spray.
The only problem was that the shop was closing at 19:00, rather than 20:00.
The hour 20:00 came from the fact that I forgot to account for 1-hour difference between the Netherlands and Romania.
I needed to get the bear spray as Romania has the biggest brown bear population in Europe, so I decided to skip the shower and the walk, and drive to the city center.
I arrived at the shop 15 minutes before it closes, and… they ran out of bear spray.
The guy at the store was very friendly, gave me a lot of tips about behavior in Romania nature in relation to bears and shepherd dogs, and suggested I take a 15-minute drive to big shopping center to a store that might have the needed spray.
I did that, bought the bear spray, and headed back to the hotel.&lt;/p&gt;
&lt;p&gt;I was starving, but I still needed to do one more shopping run: buying food for the hike.
Luckily, there was a Carrefour nearby, so I took a shower, and went to get the needed food for the hike.
And then, finally, at around 21:00 I had my late lunch / dinner in the city center, strolled a bit on the main street, and went to sleep.&lt;/p&gt;
&lt;h3 id=&quot;day-two---the-big-hike&quot;&gt;Day two - the big hike&lt;/h3&gt;
&lt;p&gt;The next two days were reserved for hiking.
I had one big hike planned: ascending La Om peak, either on Friday or Saturday, depending on the weather.
Friday weather turned out to be great, so I had breakfast at the hotel, and by 8AM drove to the trailhead.&lt;/p&gt;
&lt;p&gt;The climb deserves it’s own post, so you can read it here: &lt;a class=&quot;internal new&quot; href=&quot;/articles/climbing-la-om-peak-in-romania/&quot;&gt;Climbing La Om Peak in Romania&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;day-three---no-more-hikes&quot;&gt;Day three - no more hikes&lt;/h3&gt;
&lt;p&gt;Climbing La Om turned out to be more demanding than I though, so I allowed myself to sleep in for as long as I felt, and rest more.
I took the first half of the day easily, waking up at 9AM, having breakfast at the hotel and driving to a pharmacy to get muscle cramps relief cream.&lt;/p&gt;
&lt;p&gt;However, I have this problem that I can’t really relax.
This is why I don’t like laid-back vacations on the beach where you are not active, I have to do something.
So, despite the fact that my legs were killing me, I decided to take a car and drive to Bran to take a look at the famous Bran, sorry Dracula, Castle.
Fun story: Vlad Dracula never set foot in that castle (at least there is no historical evidence that he did so).
The reason that Bran Castle is associated with Dracula is pure luck, it was not mentioned by Bram Stoker, and the most logical explanation I found is that it resembles the castle that is described in the novel.
This did not prevent Bran, and it’s castle to turn into a touristic trap.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;View of Bran Town and Bran Castle&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/bran.CY-WWlbH_Z19f0Ti.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;View of Bran Town and Bran Castle&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I didn’t visit the castle, because the reviews said it’s mostly empty, and many people recommend visiting Peleș Castle instead, which I tried to do on my way to Brasov.
Instead, I had traditional Romanian lunch, and a traditional Romanian dessert called Papanasi (which was way too big for me).&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Papanasi&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/papanasi.CvDhfp88_Z1gLvaa.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Papanasi&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I strolled a bit around Bran, and decided to come back to the hotel, rest a bit and go to Brasov before the sun sets, but… I couldn’t get out of bed, so the rest of the evening I spent at the hotel.&lt;/p&gt;
&lt;h3 id=&quot;day-four---brasov-to-bucharest&quot;&gt;Day four - Brasov to Bucharest&lt;/h3&gt;
&lt;p&gt;Initially, I did not plan to got to Bucharest.
I’m kind of over the type of tourism where you stay in big cities.
But I figured that I probably won’t come back specifically to Bucharest, and since I was there already, why not spend a day at the big city?&lt;/p&gt;
&lt;p&gt;But before leaving Brasov, I wanted to at least take a look at this city in daylight.
I packed my stuff, threw everything in a car, and drove to the city center.
In previous days, it took me about 15–20 minutes to find parking near the city center, but today was Sunday, and on Sunday parking is free.
I did 5 or 6 rounds, and couldn’t find parking.
Eventually I said fuck it, and parked at an overpriced hotel parking near the city center.&lt;/p&gt;
&lt;p&gt;I strolled a bit around the city center, and drank a cold coffee.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Live for the moment you can&amp;#39;t put into words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;4032&quot; src=&quot;/_astro/coldcoffee.CZ5yf_-b_Z1jjHFh.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Live for the moment you can&apos;t put into words&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;At around noon, I left Brasov in direction of Bucharest.
But as I told you, I can’t just relax, I need to be in motion, so before reaching Bucharest, I had two stops planned: a hike from a town called Busteni, and lunch in Sinaia with a peek at Peleș Castle (attempt no. 2).&lt;/p&gt;
&lt;p&gt;Usually, a drive from Brasov to Busteni takes about 45 minutes.
But with my luck it took me an hour and 45 minutes.
I was stuck in traffic congestion 9km away from Busteni for 50 minutes.&lt;/p&gt;
&lt;p&gt;Busteni is a town surrounded by the Bucegi mountains, and the view is breathtaking.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Busteni town with Bucegi Mountains at the back&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/busteni.CaEv-STy_Zx9hjA.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Busteni town with Bucegi Mountains at the back&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I really want to come back there one day.
In Bucegi I did a light hike that supposed to be 45 minutes, but took me an hour and a half because my legs were still sore.
This was more easy going hike with the “jeans and sneakers” type of hikers compared to the one I did two days ago.
In my opinion it wasn’t worth it, but I pushed it because I’m that kind of person.
At the end, I saw a nice waterfall named Urlătoarea.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Cascada Urlătoarea (Bușteni)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;/_astro/waterfall.CSsFWt5W_ZUkNXs.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Cascada Urlătoarea (Bușteni)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Busteni to Sinaia is a 15-minute drive, which again took more than that because… Romanian mountain roads.
For some reason Sinaia city center was closed again, and there were a lot of people (maybe because it’s Sunday).
I decided to grab something to eat, but had no time to visit the Castle.
I still had almost 2 hours of drive to Bucharest, and I wanted to have a night out in Bucharest, so I decided to skip the castle.
Maybe next time.&lt;/p&gt;
&lt;p&gt;I arrived at Bucharest around 8PM, checked in to my hotel, took a quick shower and an &lt;del&gt;Uber&lt;/del&gt; Bolt to the city center.
I really wanted to get to a place called NOMAD Skybar, which is a rooftop bar.
On Sundays, it’s usually easy to get there compared to Fridays or Saturdays, and so I had a couple of non-alcoholic cocktails and some dinner, and decided to take a walk back to the hotel while strolling a bit around Bucharest at night.&lt;/p&gt;
&lt;h3 id=&quot;day-five---bucharest-and-flight-home&quot;&gt;Day five - Bucharest and flight home&lt;/h3&gt;
&lt;p&gt;I woke up a bit early, ate breakfast at the hotel, packed all my stuff and checked out from the hotel.
It’s really liberating when I have all my stuff with me, and I don’t need to leave my luggage at the hotel.
So I just took my backpack and ordered Bolt to the city center, strolled around, then took a metro from the Old Town to Piața Romană to drink some cold coffee.
Afterward I took a walk along Calea Victoriei towards my hotel as I needed to take my car and return it by 2PM.&lt;/p&gt;
&lt;p&gt;I paid for the parking at the hotel, and drove back to Otopeni Airport, stopping at a gas station before returning my car.
At 4 hours left before my departure time, I decided it’s not worth to go back to the city, so I just passed security and slowly killed my time in the airport.&lt;/p&gt;
&lt;p&gt;In the evening, I was back home.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It’s the third time I travel by myself.
The first time I went long time ago to Hungary, and then last year to Germany and Czechia.&lt;/p&gt;
&lt;p&gt;I must say that Romania surprised me in a good way.
I guess this country has some of the best mountains I have seen to this day.
The amount of peaks you can climb, and the vast amount of outdoor activities you can do.
I saw a lot of Romanians doing various outdoor activities like hikes, mountaineering, mountain bicycling, and ATVs.
There is indeed enough nature for every person.&lt;/p&gt;
&lt;p&gt;I also enjoyed unique Romanian architecture, and their tasty food.
At times, I also saw poverty and lack of maintenance of infrastructure.
The roads are not always great, and most nature hikes — especially the popular ones — have a lot of garbage.&lt;/p&gt;
&lt;p&gt;Regardless, every person I met — from the hikers on La Om peak, the guy at the outdoor shop Annapurna in Brasov, the waiters at restaurants in Bran and Sinaia, and even the shuttle driver from Sixt who took me back to the airport after I returned my car — were very nice and welcoming.&lt;/p&gt;
&lt;p&gt;I hope to one day come back and enjoy this country some more.&lt;/p&gt;</content:encoded></item><item><title>Thoughts on AI, Intelligence, and Knowledge</title><link>https://kudmitry.com/articles/thoughts-on-ai-intelligence-and-knowledge/</link><guid isPermaLink="true">https://kudmitry.com/articles/thoughts-on-ai-intelligence-and-knowledge/</guid><pubDate>Fri, 29 Aug 2025 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have written extensively about AI in the past: &lt;a class=&quot;internal new&quot; href=&quot;/articles/vibe-management/&quot;&gt;Vibe management&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-commoditization-of-ai/&quot;&gt;The commoditization of AI&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/living-side-by-side-with-an-ai/&quot;&gt;Living side by side with an AI&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/chatgpt-ai-and-the-future-of-tech/&quot;&gt;ChatGPT, AI, and the future of tech&lt;/a&gt;, and recently &lt;a class=&quot;internal new&quot; href=&quot;/articles/in-the-name-of-progress/&quot;&gt;In the name of progress&lt;/a&gt;.
And most of these articles are somewhat negative towards AI.&lt;/p&gt;
&lt;p&gt;Now, I’m not gonna lie and tell you that I never use AI, and all of it is bad.
I do use it.
Instead of Google search; in order to do product comparisons; plan trips and brainstorm ideas; check for facts (that sometimes I cross-reference with other sources).
But I never used AI for coding.
Sure, I used AI to ask for help like “how do I sort an array in Rust”, or “how to split a string in Ruby”, and sometimes even requested pieces of code.
You know, the usual stuff, the same stuff one would Google for before AI, but now will land you on &lt;a href=&quot;https://stackoverflow.com/questions/979256/sorting-an-array-of-objects-by-property-values&quot;&gt;StackOverflow question with 52 people arguing what is the best over-engineered way to sort an array in JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But I never let AI write code for me.
I was in the camp of “if you don’t do it yourself, you will lose this ability”.
And recently, my friend told me that all is lost, because I confessed to him that I now started to let AI write code for me, while I take the wheel of reviewing the code, and steering the AI towards the accepted conventions, and optimal solutions.
And… I like it.
But I still have this internal debate between the angel and the devil who each whispers into my ears two different ideas.
And as always, when I feel lost or confused, I write about it, because &lt;a class=&quot;internal new&quot; href=&quot;/articles/why-engineers-should-write/&quot;&gt;writing is magical&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-does-it-mean-to-do-something-from-scratch&quot;&gt;What does it mean to do something from scratch?&lt;/h2&gt;
&lt;p&gt;One common argument that I see online, and have expressed myself, against AI — is that you no longer create things from scratch.
If you let AI write the code, are you &lt;em&gt;really&lt;/em&gt; a programmer then?
This question has been asked throughout history in all different variations.
It was used as an argument against the use of high level languages — are you &lt;em&gt;really&lt;/em&gt; a programmer if you don’t know how binary sort works, but instead you use &lt;code&gt;whataver_array.sort()&lt;/code&gt;?
Are you &lt;em&gt;really&lt;/em&gt; a programmer if you copy string by assignment, &lt;code&gt;a = b&lt;/code&gt;, instead of manually allocating the needed memory, and terminating it with &lt;code&gt;\0&lt;/code&gt;?
Do you &lt;em&gt;really&lt;/em&gt; know what does it mean to program a computer to do something if you never &lt;code&gt;xor&lt;/code&gt;d a CPU register?&lt;/p&gt;
&lt;p&gt;I saw a video posted on HackerNews recently, where a guy &lt;a href=&quot;https://www.youtube.com/watch?v=TBiFGhnXsh8&quot;&gt;created a floppy disk from scratch&lt;/a&gt;.
One of the comments went along the lines of “cool project, but is it &lt;em&gt;really&lt;/em&gt; from scratch?”, implying that the author did not obtain the needed chemicals himself, mined the necessary metal himself, or created the plastic material needed to print the housing.
This argument can be applied in many different aspects in life: do you really build your own house if you did not bake your own bricks, and cut your own lumber?
Does a home-made dress, really home-made unless you planted and harvested all the cotton, and produced the sewing tools yourself?
Where does one draw the line of what does it mean to do something from scratch?&lt;/p&gt;
&lt;h2 id=&quot;what-does-it-mean-to-be-intelligent&quot;&gt;What does it mean to be intelligent?&lt;/h2&gt;
&lt;p&gt;Another common argument in favor, or against, of AI, is the argument of intelligence.
When I do something and I fail, I resort to reasoning and understanding my actions with an attempt to improve my next iterations.
Contrary to what most people believe, we learn not by reading books, but by doing things, failing, and iterating.
Reading books without doing is acquiring knowledge.
Applying said knowledge is the definition of intelligence.
And one’s intelligence is built on the intelligence of others.
A cook who makes a tasty dish does not necessarily know how to grow or harvest the needed ingredients for said dish, yet he is still intelligent.
Therefore, using AI in order to produce a working code, does not disqualify me from being an intelligent being.&lt;/p&gt;
&lt;p&gt;Or maybe it does?
Where do we draw the line?
Going back to the chef example, while he does not grow and harvest the ingredients, he most likely has knowledge of their tastes and, through trial and error, has found a combination of ingredients that produce a good tasting dish.
He could have asked ChatGPT to create a “tasty dish”.
The result would, most likely, be bad.
And hence, he would iterate: “I want it more sour”.
And so on.
Does this constitute as iteration in the sense of an intelligent being?
Thomas Edison has iterated for a thousand times before creating the light bulb.
With each iteration he came closer to a working product.
What if instead he would prompt LLM to create a light bulb with a thousand iterations, would it qualify him to be an intelligent inventor of the light bulb?&lt;/p&gt;
&lt;p&gt;Moreover, people tend to over-romanticize the meaning of the word “creation”.
If you go to a bunch of restaurants, you will, eventually, notice that most dishes are the same.
Sure, some restaurants might bring their own touch (sometimes bad), but in general, a hamburger is more or less the same in most places.
Similar thing can be said about cars for examples.
Most cars look the same.
Cars like VW Transporter, Ford Transit, Mercedes-Benz Vito, and Peugeot Expert — look &lt;em&gt;more or less&lt;/em&gt; the same.
When people claim to be creating a software, they tend to overlook the fact that a good chunk of the code in there was not written by them, but rather introduced as third-party dependencies, or algorithms from Wikipedia / blogs / StackOverflow.
And then we go deeper and deeper the rabbit hole, until eventually we ask “did you really create something if you had to rely on the works of hundreds of thousands of people throughout thousands of years of progress”?&lt;/p&gt;
&lt;p&gt;Does this mean that all these examples lack intelligence?
Just because you take a successful burger recipe, or a car platform / chassis and replicate it, does this mean you did not create anything new?&lt;/p&gt;
&lt;h2 id=&quot;the-lost-knowledge&quot;&gt;The lost knowledge&lt;/h2&gt;
&lt;p&gt;There is a very interesting talk from 6 years ago titled &lt;a href=&quot;https://www.youtube.com/watch?v=ZSRHeXYDLko&quot;&gt;Preventing the Collapse of Civilization&lt;/a&gt;, in which the author brings examples of technologies from Byzantine Empire and the Bronze Age — which are now lost.
Lost, because nobody documented how they were built, and the people who built them did not pass that knowledge down.
The talk then goes into discussing how something similar might happen in tech.
A common example people like to bring is the COBOL language.
COBOL is still used heavily in the banking system, yet the number of COBOL developers is declining.
What will happen when there will be no more COBOL developers to maintain existing systems written in COBOL?
This will become lost knowledge.&lt;/p&gt;
&lt;p&gt;The same argument is applied for AI.
As we move higher and higher in abstraction levels, we lose the ability to understand the lower levels, and eventually, knowledge will fade from existence as people will have no incentive to acquire it.
Why would one learn the various sorting algorithms, and how they work, if LLMs will be capable to offer the best solution for a particular use case?
How many developers truly know what sorting algorithm is used when they call &lt;code&gt;.sort()&lt;/code&gt; in their preferred programming language?&lt;/p&gt;
&lt;p&gt;Too far-fetched for you?
There is a confirmed decline in people, especially among Gen-Z, who can write by hand (&lt;a href=&quot;https://oxfordlearning.com/is-handwriting-a-lost-art/&quot;&gt;Oxford Learning&lt;/a&gt;, &lt;a href=&quot;https://archive.ph/lPVXW&quot;&gt;Wired&lt;/a&gt;).
As we move more towards digital life, the need to write by hand declines.
It is possible that in the near future handwriting will become lost knowledge.&lt;/p&gt;
&lt;h2 id=&quot;but-does-it-even-matter&quot;&gt;But does it even matter?&lt;/h2&gt;
&lt;p&gt;When I was in my mid-twenties, my friend and I used to discuss the following question: If I take you and teleport you back a thousand, or two thousand, years, what would you be able to teach the people of that time from our modern knowledge?
It’s an interesting though experiment in order to understand what knowledge is.
You could say: Ha, its easy, I just print the entire Wikipedia and go back in time, and then I can teach them everything we know.
But how would you talk to them?
I bring a dictionary.
But is this your knowledge then, or are you just a vessel?
With the same luck, I can teleport a dictionary and a printed version of Wikipedia, and let the ancient people figure it out themselves.&lt;/p&gt;
&lt;p&gt;Any knowledge we possess always stands on knowledge of someone else.
I know how to write a program in Rust or JavaScript, yet I don’t know how to build a CPU that will execute this program.
A person who knows how to build a CPU, might not know how to obtain the needed materials for it, and so on.&lt;/p&gt;
&lt;p&gt;My negative view towards LLMs was motivated by my moral superiority of “well, at least I &lt;strong&gt;know&lt;/strong&gt; how to actually write a software”, which is in part true, but in part a self-deception.
I just defined what does it mean to know how to write a software, while taking for granted everything else outside my definition such as how build a CPU, how it works, etc.
Just like the guy with the floppy disk who defined “from scratch” as taking material and a 3D printer, but obviously there were people for whom “from scratch” means taking (or building?) a pickaxe, and go mine your own minerals.&lt;/p&gt;
&lt;p&gt;But in the end, does it really matter?
There is a theory I’ve heard about that everything in life in cyclic.
The reason we engage in wars, despite the fact that we have documented horrors of wars; the reason we fall to the same old scams which are adapted to modern times; and the reason that old styles and technologies are coming back — is because everything is cyclic.
We are not really capable to learn from past experiences, but instead each generation has to take the same path, and make the same mistakes.
We oscillate from liberalism to oppression, from freedom to lack thereof — because despite the fact that we have knowledge of how bad (or good) things have been, we do not have the experience of it.
And then there are people who are caught in-between transitions.
People who have experienced the bad, the good, and are on a path to experience the bad once again, not understanding how it is even possible that those damn young people ignore it, because &lt;em&gt;back in my days…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the end, it doesn’t really matter.
Every opinion we have for or against any technology, is just that, an opinion.
People take a stance according to their moral views and beliefs about the world.
Such as myself.
I guess I could say that I believe in meritocracy, despite the fact that I know that society is not built on merit.
But it does not prevent me from believing that a good software engineer is someone who writes code by hand and understands the meaning behind every statement.
And it’s just my world view, with boundaries that I’ve created to justify it.
Boundaries that say that it’s not important to understand how the hardware works, or the chemistry behind it.&lt;/p&gt;
&lt;p&gt;The only constant thing in life is change.
Everything is bound to change.
Even documented history can be shaped to whatever narrative you want.
Knowledge, especially in digital age, rarely gets lost.
Handwriting techniques are still there, every Gen-Z can go and learn them, they just don’t need it.
The same way we no longer use Papirus to write, or wax seals to signify the authenticity of a document, because we found &lt;em&gt;easier&lt;/em&gt; ways to do it.&lt;/p&gt;
&lt;p&gt;I guess what I am trying to say is that gate-keeping a certain aspect of our society has no benefits.
The reason Gen-Z don’t write by hand is that they have no need for it.
It’s the aristocracy all over.
People who have comfortable life tend to sit and discuss morals and ethics, while the common-folk try to survive day by day.
Difference being that in the old time, aristocracy has been, mainly, passed from generation to generation.
Children of wealthy people learned to read and write because their parents knew how to read and write, thus becoming aristocrats.
Nowadays, knowledge is &lt;em&gt;mostly&lt;/em&gt; available to everyone.
Sure, using AI might dumb-down your critical thinking skills, but nothing prevents you from writing code by hand in your spare time in order to keep your mind sharp.
I still read books, despite the fact that the general trend seems to indicate a decline in book reading.&lt;/p&gt;
&lt;p&gt;And I can go on and on about such examples.
We lost the art of fixing our electronics or cars, for the sake of outsourcing the labor to someone else, be it a car mechanic or a factory in China, and this made us less capable.
We lost the art of growing and harvesting our food, because we have the farmers and their machinery to take care of that, and this made us less capable.
We have surrounded ourselves with warmth and comfort, thus prolonging our lives, and this made us less capable and resistant to the harsh environment.&lt;/p&gt;
&lt;p&gt;And I guess the topic of “X is making use dumber / replaces thinking / etc” has been discussed from the first day we gained the ability to philosophize and think.
The only way to prevent X from making use dumber, is to go back to hunter-gatherer tribes whose sole goal in life is surviving yet another day, and reproduction.&lt;/p&gt;</content:encoded></item><item><title>Change and Adversity</title><link>https://kudmitry.com/articles/change-and-adversity/</link><guid isPermaLink="true">https://kudmitry.com/articles/change-and-adversity/</guid><pubDate>Wed, 20 Aug 2025 19:30:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the biggest questions that I’d like to know the answer to, is: “Is it possible to change yourself, and how much control you have over this decision?”
Sure, there are people who claim to have made a 180 degrees personality changes, but — aside from the fact that I can’t verify random online claims — assuming it’s true, I wonder, did they have/had a &lt;em&gt;predisposition&lt;/em&gt; to make this change?&lt;/p&gt;
&lt;p&gt;Some time ago, I came back from the gym and caught my wife watching a new video from &lt;a href=&quot;https://yestheory.com/&quot;&gt;Yes Theory&lt;/a&gt;.
To those who don’t know what Yes Theory is, it’s a community that believes that the best, and most fulfilling life things happen outside your comfort zone.
In fact, I have introduced my wife to their channel long time ago, but like most channels, I stopped watching them when the content became repetitive and boring.
Nevertheless, she offered me to watch the video with her, and I agreed.&lt;/p&gt;
&lt;p&gt;The video in questions is about &lt;a href=&quot;https://www.youtube.com/watch?v=9HHI2EuZoWc&quot;&gt;surviving the deadliest military training on earth&lt;/a&gt;.
In the video, the guys from Yes Theory join a 3-day (if I recall correctly) military training with Finnish soldiers in -36C.
By the end of the video, I had a revelation that might provide an answer to my biggest question.&lt;/p&gt;
&lt;p&gt;In the video, the guys are given tasks by the Finnish soldier, and then are left to complete these tasks.
These tasks include skiing for the whole day, building a shelter, sleeping the night outside, as well as jumping into cold water.
They are being monitored by a doctor for frostbites, but nevertheless, the experience looks tough, and some of them do experience long-lasting damage, as well as mental breakdown.&lt;/p&gt;
&lt;p&gt;I always thought that overcoming adversity builds character.
In tough situations, you can overcome your pre-programmed beliefs and behaviors and emerge as a new person.
But I’ve observed something different in the video.
One of the guys in the video, was also in other videos and experiments such as: hiking 5 days in a tropical climate to reach a Mayan pyramid, sleeping on a bed located above a canyon, visiting dangerous countries, performing dangerous hikes and mountain climbs, and more.&lt;/p&gt;
&lt;p&gt;Given the sheer adversity this person had to overcome, one would assume that he would develop a resilient character.
But that’s not what I observed in that video.
In that video, he showed the same character traits as in other videos.
And this made me think: &lt;em&gt;maybe adversity does not build character after all?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sure, this is not a scientific experiment, and sure one might claim that a handful of adverse experience is not enough to fundamentally change the person’s character.
But this seems to correlate pretty well with my experience and observations.
In my theory, your character traits are developed in early age.
I don’t know the exact number, but I’d suspect it’s somewhere around the ages of 5 to 18.
These are the ages when the brain is still rapidly developing, and the child builds their character by being exposed to the environment.&lt;/p&gt;
&lt;p&gt;But then, what’s the role of adverse experience?
In my theory, the role of adverse experience is not to build your character, but rather to show you your character.&lt;/p&gt;
&lt;p&gt;Are you a quitter?
Then chances are you will quit mid-way through every hard situation you encounter.
Are you an empathetic person?
Then chances are you will stop to help every struggling individual, rather than pass by them.
But why does it matter, you might ask?&lt;/p&gt;
&lt;p&gt;Well, it fundamentally changes the entire self development mantra.
If your character is built/defined in your childhood/teenage years, then “&lt;em&gt;everyone can make a change&lt;/em&gt;” type of messages, are useless, because you can’t make a change.
Whether you are a &lt;em&gt;fight&lt;/em&gt; or a &lt;em&gt;flight&lt;/em&gt; personality, has nothing to do with how you want to react to hard situations in your adult life, but has everything to do with how you &lt;strong&gt;reacted&lt;/strong&gt; to hard situations in your childhood/teenage years.
You can’t&lt;sup&gt;*&lt;/sup&gt; become a &lt;em&gt;fight&lt;/em&gt; personality if your whole childhood/teenage years you were a &lt;em&gt;flight&lt;/em&gt; personality, just by putting yourself into situations where you &lt;em&gt;could&lt;/em&gt; fight, because your default response is &lt;em&gt;flight&lt;/em&gt;.
And it doesn’t really matter how many situations you will put yourself into.&lt;/p&gt;
&lt;p&gt;Now, notice I used &lt;sup&gt;*&lt;/sup&gt; on the “can’t”.
This asterisk means that this is the most probable outcome.
Your basic personality traits are somehow defined by the environment, and your reaction to it, during your childhood/teenage years.
And in a moment of truth, your brain defaults to these reactions.
Can they be changed?
I guess so, but it depends on a lot of factors.&lt;/p&gt;
&lt;p&gt;Self-awareness is one of them, but it’s very hard to be self-aware in challenging situations.
Brain plasticity and your will-power to change, also play a role, but I guess the older we get, the less we are willing to change, and the less plastic our brain becomes.&lt;/p&gt;
&lt;p&gt;This then means that all these seminars, courses, training programs, and other new-age stuff — doesn’t really work.
I remember long time ago, about 15 years ago, I’ve read about a self-improvement challenge on Reddit.
It’s called “Project Mayhem” (as a reference to Fight Club, I guess) and the purpose of this project was to build confidence.
It included a list of challenges such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ordering a BigMac in a restaurant that does not serve BigMac&lt;/li&gt;
&lt;li&gt;Wearing a tuxedo in public&lt;/li&gt;
&lt;li&gt;Trying new food&lt;/li&gt;
&lt;li&gt;Going to a new town by yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don’t have the entire list, and I can’t seem to find any reference to it, but the idea was to put yourself outside your comfort zone in order to build confidence.
And today, I believe that in order to be useful, it needs to be done at the right age, otherwise there is a high chance you won’t do it, because you already lack self-confidence.
You are just not the kind of person that will order a BigMac in a place that does not serve one, and doing so, won’t undo years of conditioning and resorting to default behavior, but would rather induce a sense of shame, guilt, fear, or anxiety.&lt;/p&gt;
&lt;p&gt;I don’t think this means you should avoid doing uncomfortable stuff.
And I still believe that the only way to grow is outside your comfort zone, but I’d be wary of claims that promise you to transform your personality in 48 hours, or with 22 daily challenges.
I’ve read numerous books on self-development, and despite the fact that I &lt;em&gt;try&lt;/em&gt; to implement lessons and tactics from these books, I’m nowhere near being the 2.0 version of myself.
More like 1.062 version of myself.
Because a self-development book, and a handful of challenging situations won’t undo years of preconditioned behavior.&lt;/p&gt;
&lt;p&gt;And to all the people who read this and say &lt;em&gt;yes, but I was able to change my life&lt;/em&gt;, I’d argue that you already had these personality traits, but you might have convinced yourself that you lack, for example, self-confidence (or didn’t have much chance to exert it).
Hence, when you picked up a book on self-confidence, it just helped you to structure what you already knew, and challenging situations showed you who you already are.
Because books rarely teach us something new, they just help us validate what we already know is true.
And adverse situations were designed to show you your personality, rather than alter it.&lt;/p&gt;
&lt;p&gt;The old cliché of “&lt;em&gt;people don’t change, they just become more of who they really are&lt;/em&gt;”, is probably more true than we think.&lt;/p&gt;
&lt;p&gt;In the end, you have spent years, if not decades, reacting in a particular way, and it will take you the same time, if not more, in order to reverse the damages.
I suspect that people who would want to change a particular personality trait they’ve adopted say, by the age of 14, will have to dedicated at least 14 years of constant &lt;strong&gt;attempts&lt;/strong&gt; to react differently during adverse situations.
And the later you start, the harder it will be, because as you age, you lose brain plasticity, and will-power.&lt;/p&gt;</content:encoded></item><item><title>In the name of progress</title><link>https://kudmitry.com/articles/in-the-name-of-progress/</link><guid isPermaLink="true">https://kudmitry.com/articles/in-the-name-of-progress/</guid><pubDate>Sat, 02 Aug 2025 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Few months ago, the internet has been flooded with AI generated images in the style of Pixar and Ghibli.
I didn’t even know that a new AI model was released, because I kind of quit social media, but they were everywhere, and it was impossible to miss it.
This made me think, what drives people to use these tools?&lt;/p&gt;
&lt;h2 id=&quot;this-is-the-price-of-progress&quot;&gt;This is the price of progress&lt;/h2&gt;
&lt;p&gt;When we are talking about technological progress, there is one argument that is impossible to fight with: this is the price of progress.
The tractor has replaced the hard work of the farmer and his animals.
Electricity killed the profession of the guy who had to light candles on the street every day.
The internet has killed the printed press.
And AI is killing everything else.
But this is the price of progress.&lt;/p&gt;
&lt;p&gt;I doubt there is a person who would answer negatively if asked: &lt;em&gt;“Would you like your work to be easier, and for you to have more time to do what you &lt;strong&gt;really&lt;/strong&gt; like?”&lt;/em&gt;.
We all wish we had more time to do the things we like.
This is why we invented the washing machine and the dishwasher.
And this is why AI is taking the world by storm — because it helps me with mundane tasks, and so I have more time for the things I &lt;strong&gt;truly&lt;/strong&gt; want to do.
This is the progress, and this argument is bulletproof.
Arguing with this argument, is like arguing that it is better to be alive than dead.
It’s almost an axiom.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This is the price we have to pay for progress. Do you want to stay behind, doing mundane work for the rest of your life?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The world is hard, and tech bros are here to make it easier, and there is a price for progress: loss of jobs, loss of friendships, loss of purpose, and loss of meaning.
You either agree with this, or you are outdated; an old man rumbling about the good times that were there, 10, 20, 50 years ago.
So you adapt, or fall behind.&lt;/p&gt;
&lt;p&gt;But despite the fact we have social networks, we feel more lonely and less connected to our fellow human beings.
Despite the fact that we have the entire human knowledge in our pockets, we can’t seem to distinguish truth from lies, good from bad, and we are more divided than ever.
And contrary to what progress advocates tell you, we, in fact, have &lt;strong&gt;less&lt;/strong&gt; time to do the things we really want to do.
Meaningful work is shrinking, despite the fact that AI evangelists tell us that in the bright future of AI, we won’t be doing mundane work — we will all be artists.
But artists are disappearing, because today, everyone is a Ghibli Studio.
Everyone is a Pixar artist.
Everyone is a book writer, a coder, an entrepreneur, a music creator, a blogger.&lt;/p&gt;
&lt;p&gt;But here is the kicker: &lt;strong&gt;creating something, is not the end goal&lt;/strong&gt;.
For starters, if everyone can create the same things with the same amount of skill, what makes your skills and work special?
What, you think you are so unique that only you can prompt ChatGPT to generate a Ghibli-styled image, or vibe-code your shiny SaaS?
No, you are not.
You will be competing with millions of other “creators” who can do the same as you, and generate a ton crap of AI slop.
And secondly, the goal was never to create.
It’s the &lt;strong&gt;process&lt;/strong&gt; of creation that is rewarding.
Not to sound cliché, but it’s the journey that matters, not the destination.
And when you’ve been robbed of the journey — and make no mistake we are being robbed of it — there is nothing left for you other than to float in your pod, wearing your smart glasses and drinking the months’ favorite soda drink and whatever engineered meal they sell you.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Wall-E is sad&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;800&quot; src=&quot;/_astro/wall-e-human-pod.DdUWyMd8_eFp3k.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Wall-E is sad&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Or maybe you thought you will join the singularity together with Sam Altman, Jeff Bezos, and Mark Zuckerberg?
Oh, you poor soul.
They did not tell you that the rocket to Mars is not for you, and the promised singularity is not for you.
You have been, and will stay, a mindless consumer that will get excited about every new AI slop that is wrapped in “in the name of progress”, while owning less stuff, doing more mundane work (&lt;strong&gt;if&lt;/strong&gt; there will be work for you to do, and make no mistake, The Rich won’t finance your Universal Basic Income), while the people who own the tech will decide what you will eat, how far you will drive, or how stimulated you brain will become.&lt;/p&gt;
&lt;p&gt;I remember long time ago, 15, maybe 18 years ago, the internet was filled with various blogs of people who used to do coll stuff and write about it.
These blogs are still there, but they are hidden beneath a pile of AI generated slop for the sake of gaming SEO.
Just like our deep desires to connect with fellow human beings, or the surrounding nature — is buried beneath endless amounts of plastic waste, or the number of likes from your “friends” online.
At least we have the ability to wear a new dress every day with same-day delivery, or buy a new iPhone every year.
Hooray for progress!&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Old man yells at clouds&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;/_astro/old_man_yells.B7CzVUW2_Z1Lvu0S.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Old man yells at clouds&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I might be getting old.
Becoming the old man who yells at the clouds and remembers a time when everything was better, or at least seemed better.
But I’m also becoming detached from things I liked to do, like software programming.
It’s been distilled from problem-solving into AI prompting.
Movies with deep meaning were replaced by fast flashing pictures, because our TikTok brain can’t concentrate at a plot line longer than 5 seconds.
I don’t remember the last time I’ve been in flow state.
8–12 years ago, I could spend an entire day coding.
I would forget to eat, drink water.
I would wake up, and sit on a problem for 8–9 hours straight.
Today, I can’t concentrate for more than a few minutes.
If I don’t get to the solution, I become angry.
I become irritated.
Boredom has been vilified and became something you need to get rid of by scrolling yet another algorithmically curated feed.
The only time I feel creative spark is when I’m in the shower.
It’s weird, but it makes sense: there is no technology in the shower.
It’s the only place where I am alone, by myself, with myself.&lt;/p&gt;
&lt;p&gt;And I am not the only one.
People &lt;a href=&quot;https://news.ycombinator.com/item?id=43470309&quot;&gt;across&lt;/a&gt; the &lt;a href=&quot;https://news.ycombinator.com/item?id=43504073&quot;&gt;internet&lt;/a&gt; describe similar symptoms.
They coined the term “AI Brain rot” which is described as in-ability to solve difficult problems.
People are getting a joy from “unplugging” from AI and solving problems using their own brain capacity.&lt;/p&gt;
&lt;p&gt;Everything has been turned into a product that helps us save time, and yet, we seem to be busier than always.
But we need to ask ourselves the following question: Is it &lt;em&gt;really&lt;/em&gt; about progress?
Or is the argument of progress being (ab)used in the same way door-to-door salesmen use emotionally abusive arguments to sell their products:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What do you mean you don’t want life insurance for your family?
Don’t you think it would benefit them to have money once you &lt;strong&gt;die&lt;/strong&gt;?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Maybe progress is not always the goal we should aim for.
Don’t get me wrong, there are things in this world that I am grateful for, and that were done in the name of progress: modern medicine, electricity and everything that comes with it, technology.
And I don’t want to sound like an anti-AI evangelist.
I use AI, and I find it useful for certain tasks.
But there is always a scale that we need to balance.
And I feel like we’ve tipped the scale way too much.
We just keep rushing into the abyss with a promise that this new and shiny thing will, &lt;strong&gt;finally&lt;/strong&gt;, give us the ability to actually do the things we want to do.
And yet, we can’t seem to get there.
With every step forward that we take, we, eventually, find out that we took two steps backwards.
Or if not backwards, then in the wrong direction.&lt;/p&gt;
&lt;p&gt;Nobody stops to look around, and ask the right question: &lt;strong&gt;Do we really need this EVERYWHERE?&lt;/strong&gt;
Have you noticed how almost every website now has a button to “&lt;em&gt;Chat with our AI Assistant&lt;/em&gt;”?
How every company went from &lt;em&gt;“Doing cool [stuff] to solve [pain point]”&lt;/em&gt; to &lt;em&gt;“AI [stuff] to solve [pain point]”&lt;/em&gt;?
Like, why?
Why shove it in every possible place?
It’s a rhetoric question.
The answer is money.
But this is a topic for another post.&lt;/p&gt;
&lt;p&gt;And this brings me back to Ghibli/Pixar styled pictures.
What’s the point?
Why even have such ability?
These styles were unique because they were produced by certain studios.
Now they are everywhere, and there is nothing special in them anymore.
Why was in necessary?
What purpose it serves other than “in the name of progress”?&lt;/p&gt;
&lt;p&gt;With every new technology we discover, or invent, we lose something.
With the computer we lost the art of handwriting, and structuring our minds (and yes, I can see the irony).
And with AI we are going to lose everything that is left in us that makes us humans: creation, art, problem-solving, human communication, connection and relationships.
And then, Artificial General Intelligence (AGI) will be here, but not because we will create it, but because we will destroy ourselves in such way that machines will be the only entities capable of producing everything of value: art, software, music, poems, books — thus turning us into mindless consuming amoebas.&lt;/p&gt;</content:encoded></item><item><title>Why I ditched my Apple Watch</title><link>https://kudmitry.com/articles/why-i-ditched-my-apple-watch/</link><guid isPermaLink="true">https://kudmitry.com/articles/why-i-ditched-my-apple-watch/</guid><pubDate>Tue, 29 Apr 2025 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Almost two years ago, I landed in Costa Rica.
After arriving at my Airbnb in the evening, I took off my Apple Watch and put it on a charger while I was preparing to take a shower and go to sleep.
The next day, after I woke up, instead of putting the watch first thing in the morning, like I used to do for the past 2+ years, I left it on the charger.
For more than a week.
And then I powered it off.
And barely wore it again since then.&lt;/p&gt;
&lt;h2 id=&quot;my-relationship-with-smart-watches&quot;&gt;My relationship with (smart) watches&lt;/h2&gt;
&lt;p&gt;I can’t call myself a watch enthusiast, but the oldest photo of me that I could find is from 2009, and I wear a watch on this photo.
It’s a Casio that I don’t remember the model number of.
I’ve replaced it over the years with different models of Seiko, which I love and appreciate as pieces of Japanese craftsmanship.
I owned one Timex, and a few G-Shocks.
Nothing fancy, and nothing without a quartz in it.
But a watch was on my wrist since high school.&lt;/p&gt;
&lt;p&gt;And while yes, a watch can tell you the time (and sometimes the date), this is not the reason I wore watches.
I always saw them as pieces of self-expression.
Going on a hike?
Slap on a G-Shock.
Fancy date night?
Put on a nice dress watch.
Or change the strap.&lt;/p&gt;
&lt;p&gt;I always resisted smartwatches because they felt soulless to me.
I couldn’t find a round smartwatch that didn’t look like a toy, and Apple’s square design was big no-no for me.&lt;/p&gt;
&lt;p&gt;But then, COVID came.&lt;/p&gt;
&lt;p&gt;I, like everyone else, was locked in my home and barely had any movement.
And when the lockdowns eased, I decided to get a smartwatch, as everyone around me was getting one.
I got the Samsung Galaxy Watch because I was using a Samsung phone, but later switched to Apple ecosystem with iPhone and Apple Watch.
And after more than 2 years of wearing the watch all day, every day, except for nights, I took it off except for occasional uses, and a few months ago I sold it and went back to a traditional watch.&lt;/p&gt;
&lt;p&gt;I write this post as a reminder to my future self.&lt;/p&gt;
&lt;h2 id=&quot;but-why&quot;&gt;But, why?&lt;/h2&gt;
&lt;p&gt;Before I tell you why I ditched my smartwatch, let me tell you why I got one in the first place.
Except for the fact that everyone was getting one, and I got hooked on the fitness tracking, there weren’t many reasons as to why I got one in the first place.
I still think they look like a toy.
I still dislike the square form factor.
And I’ve learned that smartwatches are not as useful as they are advertised to be.&lt;/p&gt;
&lt;p&gt;Sure, it was nice to glance at my watch and make sure I won’t miss yet another pointless meeting in 7 minutes.
Or it was comfortable to approve my request to connect to a company VPN, from a company laptop, while being on a company Wi-Fi network.
Gee, I hate the corporate world.
And I feel like the smartwatch is built for this corporate environment where everything is overcomplicated, and your life is a chaotic mess filled with pointless meetings.&lt;/p&gt;
&lt;p&gt;And so, little by little, every advantage that I saw in smartwatches, turned into things I hate about them.&lt;/p&gt;
&lt;h3 id=&quot;the-tracking&quot;&gt;The tracking&lt;/h3&gt;
&lt;p&gt;Without a doubt, one of the biggest advantages of smartwatches is the fitness tracking.
When I was debating what smartwatch to get, I was debating mainly between a Garmin that is more fitness oriented, and a Galaxy Watch which is more day-to-day and some fitness.
Today’s smartwatches are filled with sensors to measure your heart rate, steps, calories burned, etc.
They collect a bunch of health related data about you, and present it to you via a nice application.
Some devices even take it further, and provide you with “energy score” or “body battery levels” — the purpose of which is to let you know how well rested you are.
But there are 2 problems with tracking health data.&lt;/p&gt;
&lt;p&gt;Firstly, it’s unreliable.
None of the devices on the market today are certified as medical grade equipment.
I would randomly close my activity ring on the Apple Watch while sitting in front of a computer.
Or the other time, when I took the watch with me while summiting &lt;a class=&quot;internal new&quot; href=&quot;/articles/hiking-acatenango/&quot;&gt;Acatenango Volcano&lt;/a&gt; in Guatemala, and try to measure my SpO2 (blood oxygen levels), I got results that varied from 88% to 97% in a span of two different measurements followed one another.
Mind you, anything below 95% stars to get dangerous.&lt;/p&gt;
&lt;p&gt;Secondly, and I think this is the Achilles hill of fitness tracking, is that most of the time it’s counterproductive.
After getting the watch, I wanted to gather as much information about my health as I could.
There were two applications where I couldn’t use the watch.&lt;/p&gt;
&lt;p&gt;The first one, is during boxing sessions.
It’s uncomfortable to put gloves on top of wraps on top of a watch.
So I got myself a chest strap.
The second is sleep tracking.
The watch needs to be charged at least once every 24 hours, so it means I either sacrifice missing the daily standup notification, or I miss tracking my sleep.
In addition to that, sleeping with the watch is uncomfortable.
So at some point, I was thinking to get an Oura ring.
A ring that is designed to track sleep.
It’s relatively discreet, and I wear my wedding ring anyway, so I could just replace it with the Oura ring.&lt;/p&gt;
&lt;p&gt;The way Oura ring works, is that it gives you a score every morning based on how well you’ve slept the night before.
And what happens to many people I’ve read or watched online, is that they would wake up rested, but then would look up at their score to find out that it’s bad, and the app would recommend them to rest or do light workout this day.
So this will lead to more anxiety and self-induced bad mood, in addition to potentially skipping the gym.&lt;/p&gt;
&lt;p&gt;And this is the biggest problem with these fitness trackers.
They give you a bunch of data and then try to do pattern matching.
But this data is pointless, and often times causes you to be more upset with yourself.
I remember I was invited to a panel where I shared tips on how to get hired in tech, and I was wearing my Apple Watch.
I, like many other people, get stressed when talking in front of a big group of people.
And I remember that for the first time in my entire time of owning the Apple Watch, I got a notification of an unusually high heart rate.
It was about 160-170 bpm.
I knew I was stressed, but I tried to focus my attention on the questions and the answers.
You know what this notification did to me?
Made me more stressed.&lt;/p&gt;
&lt;h3 id=&quot;you-are-always-connected&quot;&gt;You are always connected&lt;/h3&gt;
&lt;p&gt;When I first got the Apple Watch, I thought it’s kind of cool to take a look at the watch and decide if a notification is worth my time or not.
But as I lived more with it, it became annoying.
First, it started with the fitness challenges.
My friend would complete a workout, and I got a notification for that, so I could boost his ego.
After some time, I turned it off.&lt;/p&gt;
&lt;p&gt;But with every new app that I’ve installed on my phone, came a hidden watch companion app, so I would get random buzzing on my wrist throughout the day.
It became a constant fight of man against machine, where I tried to outsmart my watch from bombarding me with more and more notifications.
In addition to that, some built-in features like smart unlock — became annoying.&lt;/p&gt;
&lt;p&gt;The way smart unlock works, is that if it detects that I take my phone while my watch is on my wrist, it will unlock the phone without the need for FaceID.
The same would work for a MacBook — when I open the lid, the watch would unlock it.
And so, every time I would take out my phone to check the time, or see who is calling me, my watch would vibrate and unlock the phone.
It’s a nice feature I guess, but I don’t think it’s really that necessary as FaceID is very reliable.&lt;/p&gt;
&lt;p&gt;I’ve read of people who developed a syndrome they call “phantom vibration”.
They got so conditioned for the vibrations of their watch, that they now imagine it vibrating at random times.&lt;/p&gt;
&lt;p&gt;Lastly, talking with people who wear smartwatches — became annoying.
I barely speak with people in real life, as I moved to a new country.
And so I appreciate every opportunity to talk to people in real life, and I try to dedicate 100% of my attention to the person I’m spending time with.
And I’ve noticed that people who wear smartwatches get often interrupted by a notification which makes them lose focus and concentrate on the watch in order to focus their mental energy on one question: &lt;em&gt;Should I pull out my phone and react to this notification?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Which leads me to the last problem.&lt;/p&gt;
&lt;h3 id=&quot;its-neither-a-tool-nor-a-fashion-statement&quot;&gt;It’s neither a tool nor a fashion statement&lt;/h3&gt;
&lt;p&gt;You see, my phone is a tool.
I can reply to an email; refund a customer; make a call; schedule an event.
Yes, it allows me to waste my time by engaging in doom scrolling, but it’s still a tool.&lt;/p&gt;
&lt;p&gt;A regular watch, at least for me, is more of a fashion statement.
Yes, it tells the time and sometimes the date, but unless a random person on the street asks me “what time is it?” — I’d rather pull out my phone to check for time and at the same time pay for my groceries or make a phone call.
A watch is something you would pair with an outfit; a phone is not.
A nice watch could be a conversation starter; a phone is not.&lt;/p&gt;
&lt;p&gt;However, smartwatches are neither a tool not a fashion statement.
Even if you change the straps, it won’t make people notice you or appreciate your watch any more than a pink phone case would.
With an Apple Watch you will look like any other person who has Apple Watch.
And everything you can do on a smartwatch, you can do better on a phone.
Here are a few examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Navigation&lt;/strong&gt;.
In a car, I would prefer a screen that is positioned at a road level rather than having to take my eyes of the road to look at my watch.
While biking, I found out that Google Maps has very unreliable turn-by-turn navigation, so I spend time messing with getting Google Maps work on my watch rather than paying attention on the road, and usually resort to occasional stops where I pull out my phone, and memorize the next segment of the route.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google/Apple Wallet&lt;/strong&gt;.
I rarely leave my house without my phone.
I believed I developed a fear that I share with other peers of my generation (and the generations after mine) — feeling naked without my phone on me.
But let’s assume that the phone is in my backpack or whatever, so it becomes easier to pay with the watch, but in reality it is not.
You see, while I’m right-handed, I wear my watches on my left hand, as I believe the majority of people do (it is common to wear watches on your non-dominant hand).
And I found out that most payment terminals were designed for right-handed operation.
In the Netherlands, for example, the most common payment terminal I use, is at the gates of a train stations.
The terminal is &lt;strong&gt;always&lt;/strong&gt; located on the right side.
And this means that paying with a watch on a left hand involves cross body hand rotation at a weird 180 degrees motion in order to place the face of the watch at the terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Music controls&lt;/strong&gt;.
I have controls on the AirPods for that, and I don’t really care what’s the name of the song, so I don’t need a screen.&lt;/p&gt;
&lt;p&gt;There is nothing you can do better on a watch than on a phone.
Nothing at all.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I don’t say you need to ditch your smartwatch.
I just feel like it’s a product that doesn’t bring anything new to the market.
Sure, there are fitness oriented smartwatches mainly from Garmin.
And in-fact, I used to own a Garmin as replacement for my G-Shock for when I go hiking.
It’s way tougher than Apple Watch, so it won’t break as easy, and it has actual fitness related features and a longer battery than the Apple Watch.
As for now, I have decided to downgrade to one watch to do it all.&lt;/p&gt;
&lt;p&gt;But other than this, I was very happy to take off my Apple Watch after quitting my job.
I feel like this is a product that was created for the chaotic, and unhealthy industry that we live in.
An industry of constant time-wasting activities in a form of pointless meetings, as well as demand to be online 24/7 and reply to any notification in an instant.&lt;/p&gt;</content:encoded></item><item><title>Vibe Management</title><link>https://kudmitry.com/articles/vibe-management/</link><guid isPermaLink="true">https://kudmitry.com/articles/vibe-management/</guid><pubDate>Tue, 01 Apr 2025 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vibe coding is taking the internet by storm.
Popularized by &lt;a href=&quot;https://xcancel.com/karpathy/status/1886192184808149383&quot;&gt;Andrej Karpathy&lt;/a&gt;, it changes the entire industry.
But why only engineers should have all the fun?&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/vibe_coding_twitter.png&quot; width=&quot;500&quot; alt=&quot;Andrej Karpathy Tweet&quot; class=&quot;object-fill m-auto&quot; loading=&quot;lazy&quot;&gt;
&lt;figcaption&gt;
Andrej Karpathy Tweet
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I’m in favor of making as many professions in tech redundant, as possible.
I mean, if I can enjoy singularity while vibing my way through my code, why upper and middle management shouldn’t?
And the reality is they should!
And they can!&lt;/p&gt;
&lt;p&gt;As of today, both ChatGPT and Claude are more than capable of replacing your average middle manager such as team lead or product manager.
Here is an example of an interaction between a product manager and a potential customer that begs for a feature:&lt;/p&gt;
&lt;blockquote&gt;
You are a product manager, and a customer requests a certain feature to be implemented. The truth is, you are over capacity, and don&apos;t really want to implement it, but it is your duty to make your customers happy, it&apos;s in the DNA of your company &quot;Customers First&quot;. Write a short statement that will make the customer feel good, without making any commitment about deadlines, or even if the feature will be delivered at all.
&lt;hr&gt;
&lt;p&gt;Thank you for sharing your feedback! We truly appreciate hearing from our customers, as your insights help shape the future of our product. This is an interesting idea, and we’ll be sure to take it into consideration as we evaluate potential improvements. While I can’t provide a specific timeline, please know that our team is always looking for ways to enhance the experience. Keep the great ideas coming—we love hearing from you!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Delightful!&lt;/p&gt;
&lt;p&gt;But we won’t stop there.
Team lead is our next goal!
Coming up with a good way to share negative feedback, is a very challenging thing.
It can even harm your mental health, or turn your subordinates against you.
But don’t worry, we can remove YOU from the equation, and instead let ChatGPT do the heavy lifting:&lt;/p&gt;
&lt;blockquote&gt;
You are a team lead of in small engineering team. One of your peers underperforms. It is your duty to deliver negative feedback to them, without hurting their feelings or demoralizing them even more, as well as protecting yourself from getting labeled as a bad manager.
Write a short statement that you can say during 1-on-1 with your subordinate, to make sure they will understand that they need to start performing, or there is a big line of candidates that can replace them.
&lt;hr&gt;
&lt;p&gt;Hey [Name], I wanted to take some time to check in with you. I’ve noticed that some of your recent work hasn’t quite met the expectations we set, and I want to understand if there’s anything blocking you or if you need support in any way. My goal is to see you succeed here, and I know you’re capable of more. That said, we do have high standards, and I need to see improvement moving forward. Let’s work together to make sure you’re on track because this is a competitive environment, and I want you to be in the best position possible. What can we do to help you step up?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that’s a banger!
Who wouldn’t want to dedicate 24 hours a day to their corporate overlord after hearing such constructive and thoughtful feedback!&lt;/p&gt;
&lt;p&gt;Let’s take it a step further.
When there is no middle management, we can argue for the necessity of upper management.
Undoubtedly, but CEOs perform a very important role in the company.
But the truth is, with the help of ChatGPT, the CEO can forget that problems exist, and embrace exponentials.
They no longer need PR teams, and they can create infinite number of iterations before they land on something they like.
And they don’t even have to touch the keyboard, they can talk to Whisper.
They can run a company, but it’s not really running a company — they just type stuff, copy stuff, and see stuff, and it mostly works:&lt;/p&gt;
&lt;blockquote&gt;
You are the CEO of security company called CrawlStroke, and earlier today, your company have shipped a bricked update that broke the entire internet. Blue screen of death is present at airports, hospitals, emergency service, banks, etc. Everyone is angry, and shareholder are not happy. Write an apologetic press release, with emphasis on taking full accountability, fixing it as fast as possible, and offering a 10$ discount for Uber eats to your customers.
&lt;hr&gt;
&lt;p&gt;SUNNYVALE, Calif. — [DATE] — This morning, CrawlStroke issued a routine security update that, due to an unforeseen technical error, resulted in widespread system failures across multiple industries. We recognize the severity of this incident, and we take full accountability for the disruption this has caused to businesses, critical infrastructure, and individuals worldwide.&lt;/p&gt;
&lt;p&gt;First and foremost, we deeply regret the impact this has had on hospitals, emergency services, airports, financial institutions, and countless other organizations that depend on stable, secure systems. Keeping the world safe from cyber threats is our mission, and today, we failed to uphold that responsibility. For that, we are truly sorry.&lt;/p&gt;
&lt;p&gt;Our top engineers have been working around the clock to resolve the issue. We have already deployed a fix to affected systems, and we are providing direct assistance to organizations still experiencing issues. If your business is still impacted, we urge you to contact our emergency response team immediately at [support hotline/email].&lt;/p&gt;
&lt;p&gt;While we cannot undo the disruption this has caused, we want to make things right. As a small token of our apology, we are offering a $10 Uber Eats credit to our affected customers. We know this doesn’t fix everything, but we hope it brings some relief during this challenging time. Details on how to redeem this offer will be provided via email.&lt;/p&gt;
&lt;p&gt;We are conducting a full internal review to understand how this happened and to ensure it never happens again. Transparency and accountability are core to our values, and we will share our findings once our investigation is complete.&lt;/p&gt;
&lt;p&gt;We appreciate the patience and resilience of our customers, partners, and the global community as we work swiftly to restore normalcy. We will provide ongoing updates at [company website/social media].&lt;/p&gt;
&lt;p&gt;Sincerely,
[Your Name]
Chief Executive Officer
CrawlStroke&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;OMG 🤩!
Who wouldn’t want to make business with such an accountable CEO?&lt;/p&gt;
&lt;p&gt;So here you have it!
It’s a new kind of management and leadership that I call “Vibe Management” and “Vibe Leadership” where you just give in to the vibes.
And I predict that it will replace 90% of upper and middle management in the next year, and within five years, all upper and middle management will be able to join us, software engineers, in the singularity, while all of us will be just vibing our way through our careers!&lt;/p&gt;
&lt;p&gt;Bright future ahead of us!&lt;/p&gt;</content:encoded></item><item><title>Working with systemd timers</title><link>https://kudmitry.com/articles/working-with-systemd-timers/</link><guid isPermaLink="true">https://kudmitry.com/articles/working-with-systemd-timers/</guid><pubDate>Mon, 10 Mar 2025 19:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The other day I thought to myself that it would be a good idea to have some backups of my data.
So I was wondering, how would I execute a periodic backup task?&lt;/p&gt;
&lt;p&gt;Most of you are probably familiar with &lt;code&gt;cron&lt;/code&gt;.
&lt;code&gt;cron&lt;/code&gt; is a Unix utility to run scheduled tasks.
There is a file called &lt;code&gt;crontab&lt;/code&gt; which you edit by issuing &lt;code&gt;crontab -e&lt;/code&gt; command, and inside schedule tasks such as:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 1 * * * /home/skwee357/backup.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first 5 entries are what is known as a cron expression, and they dictate when the command will be executed.
In the above example, the expression says “every day at 1 am”.
There is a cool website called &lt;a href=&quot;https://crontab.guru/&quot;&gt;crontab.guru&lt;/a&gt; which allows you to compose various cron expressions.
After the cron expression, we have the actual script to execute.
This can be a bash script, or a binary like &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Cron is easy, very simple and robust mechanism to execute periodic tasks on a *nix server, and is available by default on all popular Linux distributions.
However, cron suffers from some issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the system is down when the cron needs to run, the cron will be missed&lt;/li&gt;
&lt;li&gt;There is no built-in status monitoring&lt;/li&gt;
&lt;li&gt;There are no built-in logs&lt;/li&gt;
&lt;li&gt;If you want to execute pre/post commands (for example by pinging an external service for success/failure) you have to do it inside the script itself&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Luckily, there is another utility called &lt;code&gt;systemd&lt;/code&gt; that allows us to overcome all of the above issues.
&lt;code&gt;systemd&lt;/code&gt; is a relatively new utility that provides an array of components for Linux systems.
It is essentially a system and service manager, which also replaces some daemons and utilities like device management, login management, network connection management, etc.
&lt;code&gt;systemd&lt;/code&gt; is adopted by most popular Linux distributions, but it’s important to note that it’s not adopted by other Unix-like system such as FreeBSD or OpenBSD.
There are various unit-types inside &lt;code&gt;systemd&lt;/code&gt; but the two we are going to focus on today are &lt;code&gt;.service&lt;/code&gt; which define the actual service to execute, and &lt;code&gt;.timer&lt;/code&gt; which acts as a cron-like job-scheduler.
Let’s start with the timer.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; timers, and services, are located at &lt;code&gt;/etc/systemd/{system/user}&lt;/code&gt; depending on whether it’s a system-wide service/timer or user-only system/timer.
We will focus on system-wide services and timers, so all edits (unless instructed differently) happen inside &lt;code&gt;/etc/systemd/system&lt;/code&gt;.
First, we will create a timer for our service: &lt;code&gt;vim /etc/systemd/system/backup.timer&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Description=Backup service timer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Timer]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;OnCalendar=*-*-* 1:00:00&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Persistent=true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WantedBy=timers.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we define the unit, and it’s description.
Then, we define the timer itself.
The format for &lt;code&gt;OnCalendar&lt;/code&gt; is actually &lt;code&gt;DayOfWeek Year-Month-Day Hour:Minute:Second&lt;/code&gt;, but every component can be replaced by asterisk to signify any value.
&lt;code&gt;DayOfWeek&lt;/code&gt; can be omitted, and any two values separated by &lt;code&gt;..&lt;/code&gt; indicate a contiguous range, so for example &lt;code&gt;Mon..Fri 22:30&lt;/code&gt; means run at 22:30 on weekdays.
We can use comma to include multiple values, for example: &lt;code&gt;Sat,Sun 20:00&lt;/code&gt; means run on weekends at 20:00.
&lt;code&gt;OnCalendar&lt;/code&gt; can appear multiple times to include multiple date ranges.
Lastly, it is possible to also specify timezone in the end, for example: &lt;code&gt;*-*-* 02:00:00 Europe/Amsterdam&lt;/code&gt; to run every day at 2 AM in Amsterdam time.&lt;/p&gt;
&lt;p&gt;Then we have &lt;code&gt;Persistent&lt;/code&gt; which controls whether the timer is persistent or transient.
Transient timers are valid only for the current session, meaning if the system is powered off when the timer is due, it won’t run after system boot, just like cron.
However, if the timer is persistent, this means that when the system boots, the timer will run immediately, which is great for backups that must run even if the system crashed during/before the expected backup.&lt;/p&gt;
&lt;p&gt;Lastly, we have &lt;code&gt;Install&lt;/code&gt; directives.
Unlike crontab entries, &lt;code&gt;systemd&lt;/code&gt; units (be it services which we will talk about in a bit, or timers) are NOT active by default, and needs to be installed.
You have probably seen commands like &lt;code&gt;systemctl enable redis.service&lt;/code&gt; at least once in your development career, this is the actual installation of the unit, which from now on will be active in &lt;code&gt;systemd&lt;/code&gt;.
When installing &lt;code&gt;systemd&lt;/code&gt; units, we can optionally specify dependencies, and this is done by using &lt;code&gt;WantedBy&lt;/code&gt; or &lt;code&gt;Wants&lt;/code&gt; directives.
In case of timers, it is common to specify &lt;code&gt;WantedBy=timers.target&lt;/code&gt; in the &lt;code&gt;Install&lt;/code&gt; section.
&lt;code&gt;timers.target&lt;/code&gt; is a special target unit that sets up all the timers that shall be active after boot.
So if we want our timer to be active after boot, we have to make sure that &lt;code&gt;timers.target&lt;/code&gt; wants it.
Save the file, and exit &lt;code&gt;vim&lt;/code&gt;.
We are done with the timer, time to move onto the service.&lt;/p&gt;
&lt;p&gt;Create a new file &lt;code&gt;/etc/systemd/system/backup.service&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Description=Backup service&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ExecStart=/home/skwee357/backup.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Type=oneshot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with the timer, we first define our &lt;code&gt;Unit&lt;/code&gt; and it’s description.
Then we define the service itself.
In &lt;code&gt;ExecStart&lt;/code&gt; we give the command that &lt;code&gt;systemd&lt;/code&gt; should execute when the service is ran.
&lt;code&gt;Type&lt;/code&gt; is a bit tricky, and mainly controls how &lt;code&gt;systemd&lt;/code&gt; acts after forking the processing and executing our command.
There are various &lt;code&gt;Type&lt;/code&gt; values and you can find them in &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Type=&quot;&gt;&lt;code&gt;man systemd&lt;/code&gt;&lt;/a&gt;, but the reason I have used &lt;code&gt;oneshot&lt;/code&gt; is that it essentially allows us to specify multiple &lt;code&gt;ExecStart&lt;/code&gt; directives, and executing all the commands &lt;strong&gt;serially&lt;/strong&gt;.
If you do not intend to run multiple commands, you can use &lt;code&gt;Type=simple&lt;/code&gt; or omit the &lt;code&gt;Type&lt;/code&gt; at all, as &lt;code&gt;Type=simple&lt;/code&gt; is the default when no &lt;code&gt;Type&lt;/code&gt; present, but &lt;code&gt;ExecStart&lt;/code&gt; is present.&lt;/p&gt;
&lt;p&gt;So far so good.
Let’s spice it up.
Let’s say we want to use a service such as &lt;a href=&quot;https://pushover.net/&quot;&gt;Pushover&lt;/a&gt; to send a push notification when backup completes with either success or failure.
For that, we can use &lt;code&gt;ExecStopPost&lt;/code&gt; in the &lt;code&gt;[Service]&lt;/code&gt; section.
&lt;code&gt;ExecStopPost&lt;/code&gt; will be called once &lt;code&gt;ExecStart&lt;/code&gt; will finish running.
In order to determine the result of the execution, we will have 3 variables available to use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$SERVICE_RESULT&lt;/code&gt; - will be &lt;code&gt;success&lt;/code&gt; for successful execution, and a bunch of other codes for non successful execution&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$EXIT_CODE&lt;/code&gt; - the exit code, 0 for success, non zero for error&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$EXIT_STATUS&lt;/code&gt; - somewhat different from &lt;code&gt;$EXIT_CODE&lt;/code&gt; in a sense that if the process was killed by a signal (&lt;code&gt;SIGTERM&lt;/code&gt;, or &lt;code&gt;SIGKILL&lt;/code&gt; for example), this will contain the exit code of the termination (unlike &lt;code&gt;$EXIT_CODE&lt;/code&gt; that contains the exit code of our process)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we can write a script in bash something along the lines of &lt;code&gt;ExecStopPost=/home/skwee357/pushover.sh $EXIT_CODE&lt;/code&gt;, and inside we will parse the exit code and send appropriate push notification.
We can also use &lt;code&gt;ExecStartPre=&lt;/code&gt; in order to execute some code prior to calling &lt;code&gt;ExecStart&lt;/code&gt;.
There are way, way more settings in a &lt;code&gt;systemd&lt;/code&gt; service, so &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html&quot;&gt;RTFM&lt;/a&gt; if you need more.&lt;/p&gt;
&lt;p&gt;After we finished writing our timer and service, it’s time to install the service.
This is done via &lt;code&gt;systemctl enable backup.timer&lt;/code&gt;.
A symlink will be created, and &lt;code&gt;systemd&lt;/code&gt; will manage our timer which will trigger our service when the time comes.
In order to disable the timer, we can execute &lt;code&gt;systemctl disable backup.timer&lt;/code&gt;.
And by executing &lt;code&gt;systemctl status backup.timer&lt;/code&gt; we can view the status of our timer.
It should be &lt;code&gt;active (waiting)&lt;/code&gt; and show us how much time left until trigger.
We can also execute &lt;code&gt;systemctl status backup.service&lt;/code&gt; and view the status, as well as logs, for the service.
In general, we can use &lt;code&gt;journalctl -u backup.service&lt;/code&gt; to view the logs of our &lt;code&gt;systemd&lt;/code&gt; unit (hence the &lt;code&gt;-u&lt;/code&gt;), and combine it with &lt;code&gt;-b&lt;/code&gt; to show logs only from current boot session, or &lt;code&gt;-b -N&lt;/code&gt; where &lt;code&gt;N&lt;/code&gt; is a number, for example 2, to show logs from two boots ago, etc.&lt;/p&gt;</content:encoded></item><item><title>The commoditization of AI</title><link>https://kudmitry.com/articles/the-commoditization-of-ai/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-commoditization-of-ai/</guid><pubDate>Thu, 30 Jan 2025 18:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this week, a Chinese company released a new AI model called DeepSeek, that shattered the big tech giants that tried to own AI.
Seeing this, brings some memories from the past.&lt;/p&gt;
&lt;p&gt;In the 1990s, there was a tech revolution called “The Internet”.
According to &lt;a href=&quot;https://ourworldindata.org/internet&quot;&gt;Our world in data&lt;/a&gt;, in 1990 there were about 2.6 million of global internet users.
By the end of the decade, this number increased by more than &lt;strong&gt;15,000%&lt;/strong&gt; up to 416.2 million of global internet users.
From that moment on, the increase was almost exponential.
One of the companies that caught this trend of “the internet” was Cisco, and around 1997 their stock started to climbed and in March 2000 it’s price reached $82 per stock, putting it at a total market cap of $536B.
This was the highest stock price, and market cap, that Cisco has ever reached.
It stayed there for a year, until crashing back to $15 per stock on March 2001.
Today, it’s stock trades at around $60, and its market cap is about $230B.
What caused this fast climb and sharp decline, you might ask?&lt;/p&gt;
&lt;p&gt;The 2000s were an interesting time, when the entire tech world has lost its mind, which eventually led to the dot-com bubble bursting.
Seeing this new shiny thing called “the internet” becoming accessible to more and more people, led investors to seek the next unicorn to put their money in.
Cisco was an established networking equipment company, and it continued to expand its offerings by acquiring other companies that were doing networking, voice over IP (VoIP), digital video, LAN switching, etc.
And despite how much tech bros and investors will sell you the “we want a better world for everyone”, what they truly want (at least as number #1 priority) — are profits for themselves.
Hence, they were looking for someone to “own” the internet.
What they failed to account for is that the internet will become a commodity, which it did.
But what it has to do with DeepSeek and AI?&lt;/p&gt;
&lt;p&gt;You see, the entire AI tech bubble is built on the assumption that doing AI is hard, and it requires billions of dollars of investments.
Just few days ago companies like Microsoft, OpenAI, and Oracle announced project Stargate which will require a $500B investment in order to build nuclear power plants, data centers, and acquire hardware to train AI models.
AI seemed to be out of reach for the common folks, forever to be in the hand of the big corporations with deep pockets, who will profit massively from it.
But then came DeepSeek, which released a model that is at least as capable as the top model from OpenAI, if not more capable.
They not only set a new bar for AI models, but they also released the model as open source, and claim to have trained it on older hardware for less than $6M compared to the billions that US tech leaders are pouring into AI.&lt;/p&gt;
&lt;p&gt;This caused Nvidia to lose almost $600B of market value, a 17% drop, one of the biggest market loss in history.
There is no simple, or logical explanation as to why this happened, because Nvidia still seems to be the major provider of GPUs for AI purposes, and it’s not like AI is going anywhere.
Nvidia had a sharp climb since mid 2023, just 6 months after releasing ChatGPT, so this might be the market correcting itself.
Or as I always said, nobody knows what the stock market is, and why things happen as they do.
And while DeepSeek claim to have used older hardware, which could be a plausible claim, there is no real way to validate their claim as China not supposed to have access to modern GPU chips due to export bans, and they might have inclination to hide such information.&lt;/p&gt;
&lt;p&gt;Back in the early 1990s, the internet was not accessible to most people.
Some businesses invested into getting equipment in order to be able to access the internet, and a select few from the public used to visit Internet Cafés — public places with computers connected to the internet.
Personal computers were often sold without the expensive addition of Ethernet cards that allowed your computer to be connected to the modem.
Moreover, adjusted to inflation, the cost of getting a modem varied anywhere between $600 to $1000, and that’s in addition to having an internet capable computer.
In short, this technology was out of reach for most people.
Fast-forward to today, Ethernet is built into every single computer, and you can get a router for as little as $50.&lt;/p&gt;
&lt;p&gt;Just like with the internet, AI is going to transform.
From being tightly controlled by a group of tech people to be run on expensive hardware, to being freely available and run on consumer grade hardware.
There are people today who can run the most capable DeepSeek model at home.
Of course, it is not affordable to many yet.
Most use a cluster of 6 to 8 Mac Minis which can easily reach the cost of $10K; and those who run it on PCs, require server grade motherboards that support hundreds of GBs of RAM, as well as Nvidia GPUs which are price hiked at the moment.&lt;/p&gt;
&lt;p&gt;But make no mistake, we will eventually arrive at a future where AI will be run locally on your devices.
With Apple investing into Neural Engines in their M-series CPUs, to dedicated AI boxes from Nvidia and AMD, we will get there.
And just like with the internet, the AI bubble will burst, and AI will become a commodity.
You probably won’t be able to train an AI model at home, as it will still require money and hardware, but just like with the internet, you are not required to lay down your own undersea cables in order to speak with your friends on the other side of the world.&lt;/p&gt;
&lt;h2 id=&quot;but-what-about-agi&quot;&gt;But what about AGI?&lt;/h2&gt;
&lt;p&gt;AGI, or Artificial &lt;strong&gt;General&lt;/strong&gt; Intelligence, is something that many of these AI companies claim to move towards in their development.
Thus, the first one to reach AGI, will be the first one to set the rules.
It’s like the atomic bomb race — you either get it first and set the rules, or someone else does.
AGI, if achieved, will be a big change in the way the world works.
And so many of these tech companies seem to incorporate into their value the promise of reaching AGI.
A promise that we’ve heard to come alive next year… every year.
And yet there are no signs for AGI, and I don’t think we will see it, at least not as a by product of LLMs.&lt;/p&gt;
&lt;p&gt;When ChatGPT came out I, like many others, have seen it as this magical thing.
I didn’t really know how to treat it, or how it works.
My view of LLMs swung all the way from “they are bad and stupid” to “they are good and useful”, and with every new model, and every new use-case, my opinion is still shifting.
But it really clicked for me when I decided to learn, at least high level, what GPTs are and how do they work.
I highly recommend &lt;a href=&quot;https://www.youtube.com/watch?v=wjZofJX0v4M&quot;&gt;this video by 3Blue1Brown&lt;/a&gt; about what are LLMs and how do they work.&lt;/p&gt;
&lt;p&gt;And after viewing this video, I now see LLMs for what they are — a mathematical model to predict the next most probable word/token.
This not only broaden my knowledge, but it also allowed me to use and prompt LLMs in a more efficient way, as I now understand what are their strengths and limitations.
But what it has to do with AGI?&lt;/p&gt;
&lt;p&gt;For as long as I remember myself, I was bothered with the hardest question of humanity: what is free choice and is it truly free, rather than preselected/preprogrammed by the environment?
In short, can we predict certain human actions/interactions by observing/learning about their environment.
I don’t have the answer, but it is an interesting thought experiment nevertheless, and it aligns perfectly with LLMs and AGI.
You see, LLMs are predictable.
I tried both ChatGPT and DeepSeek for the past couple of days, and both outputted similar answers to some of the question(s).
Sure, some models perform better in specific tasks like coding, but the general gist is that they can’t &lt;em&gt;think&lt;/em&gt; in a sense a human can.
Nor they step outside their mathematical model.&lt;/p&gt;
&lt;p&gt;Humans are predictable as well, but the difference between human behavior and a mathematical model is that we can break the rules.
The other day I was sparring with my boxing coach.
He threw a jab followed by a right hook, which I blocked.
Then, he repeated the combination, which I blocked.
He did it again, which I blocked again.
And then he threw a jab, followed by right hook (which I blocked), followed by left hook which shoot me to the ropes.
In a sense, he conditioned me by his behavior (input data) to a certain reaction (output data), which can be seen as a training of an LLM.
For the same input, he got the same output, albeit a bit different as I was anticipating the combination, thus performing better/different defense sequences.
However, him being an experienced fighter, he was able to see and read my reaction in order to take advantage of my situation.
Should I have reacted differently, or been a more experienced fighter, he would probably fail to throw me to the ropes.&lt;/p&gt;
&lt;p&gt;But boxing is not a mathematical function, and it requires many skills in coordination, reading the environment, and fast reaction.
And there is always an element of surprise, or irrationality.
This can be seen as randomness by the outside observer, but it’s not.
It’s something in a though process of the brain where people are able to break outside the box (which LLMs are constrained to).
Moreover, it’s something &lt;em&gt;more&lt;/em&gt; than thinking.
I don’t like to talk in meta terms (meta the word, not meta the company), but intelligence is not only the processes that happen inside our brain, but it’s also the effects of environment.
Things like reading and understanding a situation, gut feeling, etc.
Think about a time when you felt embarrassed to do or say something, or when you reacted instinctively in order to avoid danger.
I’ve seen it many times throughout history: creative approaches to job searching, advantages on the battlefield, how certain political figures act, etc.
And I don’t think that the current state of LLMs will lead us to AGI.&lt;/p&gt;
&lt;p&gt;Don’t get me wrong, GPTs are an amazing piece of technology and research, but they are still a mathematical function, once you understand how they work.
There is no magic or thinking behind that.
And they are certainly not able to break outside the constraints of their mathematical function.&lt;/p&gt;</content:encoded></item><item><title>Ghost Engineers</title><link>https://kudmitry.com/articles/ghost-engineers/</link><guid isPermaLink="true">https://kudmitry.com/articles/ghost-engineers/</guid><pubDate>Tue, 10 Dec 2024 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;There is a new &lt;a href=&quot;https://x.com/yegordb/status/1859290734257635439&quot;&gt;viral tweet&lt;/a&gt; circulating, which states that 9.5% of all engineers—are ghost engineers.
Their performance is &amp;#x3C;0.1x of a median engineer, they do virtually no work, and they &lt;em&gt;might&lt;/em&gt; work multiple jobs, thus collecting 2x-5x of an average engineering salary.
Usually, I’d ignore such statements, in the end, there is a lot of BS circulating in the World Wide Web.
But this research seems to come from Stanford, and it has been picked up by news outlets such as &lt;a href=&quot;https://www.businessinsider.nl/as-many-as-one-in-10-coders-are-ghost-engineers-stanford-researcher-says-lurking-online-and-doing-no-work/&quot;&gt;BusinessInsider&lt;/a&gt;, &lt;a href=&quot;https://www.404media.co/are-overemployed-ghost-engineers-making-six-figures-to-do-nothing/&quot;&gt;404 Media&lt;/a&gt;, and the Washington Post (which is behind a paywall, so I’m not linking it), to name a few.
Surely, something is going on.&lt;/p&gt;
&lt;h2 id=&quot;ad-hominem&quot;&gt;Ad Hominem&lt;/h2&gt;
&lt;p&gt;Before I dive into the research, I need to address something else.
I don’t want this blog post to become an &lt;strong&gt;Ad Hominem&lt;/strong&gt; argument, an argument directed towards &lt;strong&gt;the person&lt;/strong&gt; rather than their position or stance.
But nevertheless, I do want to address some concerns about the author.&lt;/p&gt;
&lt;p&gt;Firstly, while there is a short paper explaining the methodology, it has not been peer-reviewed.
The only credible mark of this research is the Stanford name on it.
Academia has always been viewed as a source of credibility, but let us not forget that despite the fact that many good things came out of the academia, it has also served the interests of both individuals and corporations, so before this research has been peer-reviewed, we ought to view it as what it is—an infographic from a Stanford MBA.&lt;/p&gt;
&lt;p&gt;Secondly, the author.
I tried to find as much information about him as possible.
But aside from the fact that he had/is having an e-commerce business, I couldn’t find any relation between the author and software engineering in general.
And while it is not forbidden to give advice or express your opinion about things you do not understand, I also think it harms credibility when a person who has no experience in a particular field, tries to measure productivity in said field.
It’s as if I would say that doctors should be measured by the amount of pills they prescribe per day, and will provide a nice infographic to support my claim.
I can do it, but any reputable doctor will know to dismiss my opinion as it’s clear that my knowledge in medicine is limited to Googling my own symptoms.&lt;/p&gt;
&lt;p&gt;With ad hominem out of the way, let us dive into the research.&lt;/p&gt;
&lt;h2 id=&quot;the-research&quot;&gt;The research&lt;/h2&gt;
&lt;h3 id=&quot;findings&quot;&gt;Findings&lt;/h3&gt;
&lt;p&gt;First, let’s talk about the findings.
According to the author, they looked at private git commits, and fed the data to a panel of 10 experts who evaluated the quality of the code.
How scalable was the code that was committed, how slow it was, and, yes, how complicated it was to write it.
And the results were shocking!&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;The results&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;1080&quot; src=&quot;/_astro/results.JS50AycQ_1ysmz5.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;The results&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;On average, 9%, or roughly 1 out of 10, of engineers are &lt;strong&gt;Ghost Engineers&lt;/strong&gt;.
Ghost, in this context, means that they produce less than 0.1x of a median engineer, and do virtually no work at all.
This number goes down to 6% for office workers, and climbs up to 14% for fully remote workers.&lt;/p&gt;
&lt;p&gt;In the next slide, the author goes to demonstrate that from a sample of 5,832 &lt;del&gt;slackers&lt;/del&gt; ghost engineers, ~58% make less than 3 commits a month, while the other 42% make trivial changes like deleting a line or changing a character.&lt;/p&gt;
&lt;p&gt;And the next 3 slides, and the most important in my opinion (I will share further in the article why), talk about the wasted $$ these ghosts create.
In the top tech companies such as IBM, Microsoft, Amazon, Google, and others — ghost engineers are costing a whooping &lt;strong&gt;$500B&lt;/strong&gt;.
Thus, laying them off, will free these precious $$ money for… hyper growth?
I don’t know..
Anyway.&lt;/p&gt;
&lt;h3 id=&quot;methodology&quot;&gt;Methodology&lt;/h3&gt;
&lt;p&gt;Looking at the &lt;a href=&quot;https://arxiv.org/pdf/2409.15152&quot;&gt;published paper&lt;/a&gt;, we can get some more information about the methodology used.
The research focused on Java as Java “&lt;em&gt;is versatile and widely used across various domains, including mobile, web, enterprise, and
data applications.&lt;/em&gt;”
They used 10 Java experts consisting of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3 Managers with 11 to 13 years of experience, team size of 1–50 people&lt;/li&gt;
&lt;li&gt;2 Executive with 15 and 24 years of experience, team size of 1-10 and 201–500 people, respectively&lt;/li&gt;
&lt;li&gt;3 Senior engineers with 16, 20 and 25 YOE, team size of 11-50, 51-200, and 1-10, respectively&lt;/li&gt;
&lt;li&gt;1 Director with 23 years of experience, team size of 11–50 people&lt;/li&gt;
&lt;li&gt;1 Vice President, with 24 YOE, team size of 51–200 people&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Please tell me you can see that flaws in this, please!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The researchers then sampled, wait for it, 70 commits (some teams push that many commits in an hour, roughly a representative number), and asked the 10 experts to evaluate the commits by asking various questions.
I won’t list the entire questionnaire, but here are some examples of the 7 questions asked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many hours would it take you to just write the code in this commit assuming you could fully focus on this task?&lt;/li&gt;
&lt;li&gt;What is the experience level of the author?&lt;/li&gt;
&lt;li&gt;How maintainable is this commit?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, they created an AI model (of course they did), that matches the output of the experts.
That’s it.
That’s the research.&lt;/p&gt;
&lt;h2 id=&quot;my-analysis&quot;&gt;My analysis&lt;/h2&gt;
&lt;p&gt;I am a software engineer with 14+ years of experience.
Throughout my career I’ve been an interviewer, a tech lead, a mentor, and progressed from junior script kiddie to leading my own complex designs and implementations.
Moreover, I’m a tech entrepreneur building, running and scaling my own online business.&lt;/p&gt;
&lt;p&gt;When I talk about productivity, I, and people with my levels of knowledge and expertise, know that lines of code do not equal productivity.
Sometimes, you have weeks of discussion that lead to few line changes.
Consider an example where your contribution is commenting out a feature flag that held a global roll-out of a particular feature to your entire customer base.&lt;/p&gt;
&lt;p&gt;Sure, from the commit itself, you have deleted one line (or flipped a boolean variable).
But from the business perspective, it probably involved days, if not weeks of testing; discussions with PMs; alignment with other teams—for example DevOps engineers to support the increased traffic/CPU/memory; and for some engineers it might even involve speaking directly with customers.&lt;/p&gt;
&lt;p&gt;And it’s ironic that they chose Java for their research.
Considering the fact that Java null reference has been crowned as “&lt;a href=&quot;https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/&quot;&gt;The Billion-dollar mistake&lt;/a&gt;”, by the creator of the null reference himself, hence commits such as &lt;code&gt;if x != null&lt;/code&gt;, could theoretically save millions to the companies running Java for their services.
Yet, this “ghost engineer productivity” AI model would qualify such commits as “low value/low quality/super simple”.&lt;/p&gt;
&lt;p&gt;The other flaw of the research is the panel selection.
Among the 10 selected experts, only 3 are actual engineers.
I do not disqualify managers/leadership as being unable to write/understand code, but do you really think that a VP with 24 years of experience, or an executive that manages 200+ people, have been writing code in their day-to-day job, for the past decade?
Java is 29 years old, and assuming you are not born as a VP, this person probably wrote some java 20 years ago, before he was promoted to management and then leadership.
I think it’s fine to include some managers/leadership in your experts panel, but don’t you think that for a panel that measures &lt;strong&gt;software engineering productivity&lt;/strong&gt; you would want the ratio of hands-on employees to be, I don’t know, higher than 30%?
Unless…&lt;/p&gt;
&lt;h2 id=&quot;my-speculation&quot;&gt;My speculation&lt;/h2&gt;
&lt;p&gt;…you try to appeal to management/leadership.
Look, I don’t know the guy, and I don’t know what’s his motivation.
Maybe he is trying to make the world a better place by pointing at inefficiencies in software engineering.
And there are inefficiencies, no doubt in that, like in any other profession.
Is 1 out of 10 engineers are not doing anything?
Could be.
I don’t know.&lt;/p&gt;
&lt;p&gt;But this entire research seems like a sales pitch to appeal to the leadership to get rid of software engineers.
The papers seem to focus on creating AI model that consists of 70% “business peoples’” opinion, while the infographic seems to focus on the monetary loss due to ghost engineers.&lt;/p&gt;
&lt;p&gt;I’m sorry, I’m going to be blunt here: most people in leadership position know shit about what software engineers are doing.
Sure, there are VPs or CTOs who were technical in their career, but most of them are either business people who are focused on streamlining the business side of operation, or they were engineers very long time ago, and they would think that React is a name of a spell from Harry Potter.
They all are obsessed with the word productivity, but they are unable to measure it correctly.
Therefor they employ companies like McKinsey, a global &lt;strong&gt;management&lt;/strong&gt; consulting company, who published papers about &lt;a href=&quot;https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/yes-you-can-measure-software-developer-productivity&quot;&gt;developer productivity&lt;/a&gt;.
Or they are looking for the next Stanford MBA guy to sell them on some AI, because everything is AI nowadays, on how to measure developers productivity.
The deliberate use of business jargon like &lt;em&gt;0.1x engineer&lt;/em&gt; throughout the infographic is made to appeal to business people who like numbers.
What does it even mean to be a 0.1x engineer?
What is the baseline?
Who is 1x engineer?&lt;/p&gt;
&lt;p&gt;I worked with engineers who saved their companies millions of dollars per month.
They were analyzing spending pattern across cloud providers, talking with different stakeholders, and eventually delegating their work to their peers who would go on and change a config value to create less resources on AWS.
Such changes would result in decreasing the monthly bill to the cloud provider.&lt;/p&gt;
&lt;p&gt;I, myself, spent weeks working with other people to optimize the interviewing process and on-boarding more interviewers, an action that would increase the amount of quality candidates while decreasing the monetary spending on interviewers who interviewed in pairs, thus costing the company two times more per interview.
I spent weeks working on a design review that was so detail oriented that the development of the feature was almost on autopilot, allowing two teams to run in parallel.
Heck, I mentored and on-boarded other engineers.&lt;/p&gt;
&lt;p&gt;All these things resulted in 0 lines of code written or committed.
Luckily, I worked with managers who appreciated people like us, who understood that the output of a software engineer is not the lines of code they produce.
And in fact, this research seems to support my claim.
You see, managers who are obsessed with productivity, seem to inflict this obsession on their subordinates.
Hence, you see that an office environment has a lower percentage, in fact 2 times lower, of ghost engineers than fully remote positions.
In the office, it’s harder for you to sit and &lt;strong&gt;think&lt;/strong&gt; about a problem, especially if your manager expects you to write code.
This is briefly mentioned in the infographics in &lt;a href=&quot;https://x.com/yegordb/status/1859291283199782928&quot;&gt;this tweet&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Comparison between remote and office engineers.&lt;/p&gt;&lt;p&gt;On average, engineers working from the office perform better, &lt;strong&gt;but “5x” engineers are more common remotely.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And unintentionally, this research actually confirms what it tries to confirm, but from a different angle.
Productivity raises when you are not spending time in an abusive office environment, or participating in meaningless corporate rituals.
An environment that gives you peace of mind and ability to focus and think, which is your home, tend to have higher rate of “5x” engineers, unlike the uninviting and chaotic setting of the open office, with potentially abusive manager who breaths down your neck.&lt;/p&gt;
&lt;p&gt;But until managers/leadership will understand this, it seems like the worst tech market since 2008, is yet full of surprises and a grim future where our productivity will be measured by AI models created by business people who never interacted with software engineering in their life.&lt;/p&gt;</content:encoded></item><item><title>How to be a great manager</title><link>https://kudmitry.com/articles/how-to-be-a-great-manager/</link><guid isPermaLink="true">https://kudmitry.com/articles/how-to-be-a-great-manager/</guid><pubDate>Thu, 28 Nov 2024 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before joining Autodesk back in 2017, I didn’t really have a proper manager.
I either worked in family-like companies, where hierarchy was somewhat flat, or early stage companies where hierarchy was just developing.
It’s only at Autodesk where I’ve been exposed to career ladders, and proper company hierarchy.&lt;/p&gt;
&lt;p&gt;And while I do have managerial and leadership experience, I believe that a good manager can be judged only by the team he manages, rather than by his manager.
I was managed by a great manager at Autodesk.
He was so good that I even submitted a talk proposal titled “&lt;strong&gt;How to be a great manager - an insight from a non manager perspective&lt;/strong&gt;”, to the internal knowledge sharing conference at Autodesk.
My talk was rejected (don’t know why), but my observations stayed with me for the past 7 years.
And now, after 7 years, a few more managers in my career, and a fairly popular blog, I decided to share my insights here.
Let’s begin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Being a great manager is being down there with your team.&lt;/strong&gt;
I joined Autodesk at a stage where the office I worked at was transitioning from a startup like environment, to a proper chain of responsibilities, and new teams were forming.
My manager was my co-worker.
We’ve both been software engineers working on the same product.
I don’t think it’s a necessity for your teammates to see how you are being promoted to a manager in order to be a great manager, but I do think it’s important that as a manager—either one who was promoted to from inside the team, or hired outside—you need to stay close to your team.&lt;/p&gt;
&lt;p&gt;Many managers justify their work by doing “management” stuff.
They are in meetings all the time, they barely interact with their teams outside the necessary corporate rituals like team status updates, periodic planning meetings, and 1 on 1s with their team members.
I believe this mindset alienates the manager from the team that he/she manages.
Sure, management feels like something more important than writing code, but managers forget who they “serve”.
They often want to play with the “big boys”, the upper management above them, rather than interacting with the people who they need to grow—their team members.&lt;/p&gt;
&lt;p&gt;For example, my manager despite being a manager who was no longer coding, was still involved and interested in what is happening in the team.
He would ask questions about the architecture or would discuss new approaches he learned for software development.
He wasn’t taking active part in development, aside from doing light pair programming here and there, but he knew how the product works in general terms.
I know that for many managers it’s hard to stay somewhat technical, but ain’t this the reason many engineers become dissatisfied with their managers because “&lt;em&gt;he/she doesn’t understand what I’m even doing or how the product works from a technical perspective&lt;/em&gt;”?&lt;/p&gt;
&lt;p&gt;This necessity to be part of your team rather than hang-out with the “big boys”, translates to yet another important quality of a manager—&lt;strong&gt;being the firewall between their team and the rest of the organization.&lt;/strong&gt;
There is a saying that people leave companies more often due to bad managers than any other aspect.
We can tolerate somewhat boring projects, in-capable teammates, being in disagreement with the tech choices made in the team.
But we seem to be unable to tolerate bad management.
And we should not.&lt;/p&gt;
&lt;p&gt;The entire function of the manager is to focus on managing their team, and in this sense to also create a wall between the chaos that is happening in rest of the organization in order to protect the sanity, and productivity of their team.
Many managers are like messengers of bad news.
If the company underperforms, these managers would fail to absorb the negative feedback, and would pass it down to their team.
They lack stoicism, and are unable to control their emotions, thus creating a sense of tension, or sometimes even fear, inside their teams.
Great managers, on the other hand, are able to shield the entire corporate drama from their teams.
Not even once I felt stressed or being involved in corporate drama that is beyond my scope.
Sure, when I was underperforming, my manager let me know that, but anything that was not related to me, or my team—stopped at my manager.
I would often see other teams being in stress, thus having reduced productivity, because their managers failed to be the firewall that they should have been.&lt;/p&gt;
&lt;p&gt;I mentioned feedback, and a great manager is a manager that knows how to give feedback, and especially negative feedback.
Being a manager is being responsible for peoples’ growth.
The success of their team, is their success, and yet, many managers seem to forget this.
They lack basic empathy, they see themselves as “superior” to their teams, and they give negative feedback in such a way that it causes you to quit.&lt;/p&gt;
&lt;p&gt;Giving, and receiving negative feedback is a complicated social situation.
Nobody likes to be judged, especially when people &lt;em&gt;feel&lt;/em&gt; like they are doing good, and suddenly they discover that they were underperforming.
If you did not expect to get a below average score on your last performance review, rest assured that your manager failed to communicate a proper feedback to you.
On the other side, if after receiving negative feedback, you feel like your entire world has collapsed, then your manager failed in their task.&lt;/p&gt;
&lt;p&gt;I have never felt down or devastated by negative feedback, and there were times when I was underperforming.
But every time when I was underperforming, my manager knew how to communicate it to me, and how to find a solution.
And their method of communication was unique for each team member, which brings me to yet another trait of a great manager—&lt;strong&gt;they understand people&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You would think it would be a trivial requirement, right?
I mean, if you hire a NodeJS developer, you would check their NodeJS knowledge and understanding, so when you hire a manager, you would check how they understand people, right?
Right?
But many managers lack people skills.
They are unable to provide negative feedback in a way that it can be received and worked on.
And even if they can, they try to apply the same method to each and every team member.
However, every person is different.
Some are more resistant to negative feedback, while others expect to be told the bold truth in their face.
You can’t, as a manager, treat these people the same way.&lt;/p&gt;
&lt;p&gt;You need to build a connection with each and every team member.
I loved my 1 on 1s with my manager.
I waited for them.
We enjoyed talking, and I was feeling that my manager truly cares about me not only as an employee, but more importantly as a human being.
Often in our 1:1s I’d share some personal things that I go through in my life, which could be affecting my work, and my manager would care to listen, and even give advice.
I never felt that our 1:1s are just a part of the “corporate rule book”, or it’s there because it needs to be there.
He would listen, give advice, and become an &lt;strong&gt;enabler&lt;/strong&gt; to my success.&lt;/p&gt;
&lt;p&gt;Yes, being an enabler for my success is yet another quality of a great manager.
My success is his success, and he knew it perfectly.
If I wanted to do something, be it try myself in a new role, mentor someone, try out a new tech—he knew how to make it happen.
And even if it leads to failure, we would still find a way for me to try it out, while also figuring out how to recover if this fails.
There are limits of course, in the end, you can’t expect your manager to enable you to rewrite the entire product in Rust because you love Rust, but the company uses JavaScript.
But for reasonable desires like being part of the technical interviewers pool, mentoring junior developers, or leading my own project—he was able to make it happen.&lt;/p&gt;
&lt;p&gt;In conclusion, management is a leadership position.
Whether you want to be a great people manager, or you want to focus on being a great tech manager such as Tech Lead/Staff Engineer, you need to have these 5 qualities.
Let me repeat them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be there for your team, remember that their success is your success&lt;/li&gt;
&lt;li&gt;Be Stoic enough in order to shield your team from irrelevant news/events (especially negative ones) while allowing them to focus on what they do best&lt;/li&gt;
&lt;li&gt;Know how to provide feedback, especially negative feedback, in a way that it can be accepted and worked on&lt;/li&gt;
&lt;li&gt;Learn, observe, and understand people. Find the right approach to every individual&lt;/li&gt;
&lt;li&gt;Be an enabler for your team, identify how they want to grow, and find ways to make it happen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;This post does not necessarily talk about a specific individual, but rather outlines the most important qualities needed in order to be a great manager based on my observations when being managed by different managers throughout my career.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Why DX doesn&apos;t matter</title><link>https://kudmitry.com/articles/why-dx-doesnt-matter/</link><guid isPermaLink="true">https://kudmitry.com/articles/why-dx-doesnt-matter/</guid><pubDate>Tue, 05 Nov 2024 17:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Jump on any online discussion, and you will hear “DX &lt;em&gt;that&lt;/em&gt; framework”, “DX &lt;em&gt;this&lt;/em&gt; tool”, etc.
DX, together with “fast prototyping”, “rapid development”, “developer ergonomics”, seems to be the only thing everyone cares about nowadays.
I’m here to argue that neither DX nor any fast/rapid development/ergonomics enhancement tools/frameworks—truly matter.&lt;/p&gt;
&lt;p&gt;The entire premise of DX enhancement frameworks/tools is that the biggest and most damaging aspect of writing software in a &lt;strong&gt;productive&lt;/strong&gt; manner—is indeed the writing itself.
However, I bet that if we observe a lot of developers and measure the time they spend between &lt;strong&gt;writing&lt;/strong&gt; code, i.e. producing new lines of code, compared to &lt;strong&gt;reading&lt;/strong&gt; code, i.e. understanding and navigating previously written code, reading documentations, reviewing code written by their peers, etc—we will see numbers in the vicinity of 20% being the writing part to 80% being the reading part.&lt;/p&gt;
&lt;p&gt;Keep in mind that these numbers are for &lt;strong&gt;coding activities&lt;/strong&gt; only, I omit any time spent on corporate rituals like meetings and technical discussions, &lt;a class=&quot;internal new&quot; href=&quot;/books/technical-writing-for-software-engineers/&quot;&gt;producing technical documentation&lt;/a&gt;, chatting with co-workers, and general procrastination.
From my experience, an engineer is lucky to spend 2 full hours out of an 8-hour day in coding related activities, the rest of the time is spent on other tasks.
And out of these 2 hours, the majority of the time is spent on reading/understanding code, rather than producing code.
This alone should be a strong indicator that DX doesn’t really matter, because if you want to optimize for productivity, you better optimize for ease of reading and understanding code (or abolishing meaningless corporate rituals, but the end of the world will arrive sooner than management will free us of meetings).&lt;/p&gt;
&lt;p&gt;I believe that this was the purpose of Go.
It’s an ugly, in my opinion, and very verbose language, but it’s super easy to read.
But let’s say you still disagree with me.
I shall demonstrate it by looking at one of the tools I like: Svelte/SvelteKit.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;My point is &lt;strong&gt;not&lt;/strong&gt; to criticize Svelte/SvleteKit, I really enjoy the framework, but rather demonstrate a point with a tool I’m familiar with it.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Svelte and Kit are the king of DX.
Jump on HackerNews or any web development related subreddits, and you will see how people praise Svelte and Kit.
It is a great framework indeed, a very refreshing approach to web development.
But, as many other frameworks, it optimizes for the wrong things.
While it has a great DX, it breaks quickly when you are done developing and now need to move to production.
You will, eventually, encounter weird bugs with hydration errors, or that &lt;code&gt;window&lt;/code&gt; is suddenly undefined.
Moreover, while you develop with a hot reloading server behind &lt;code&gt;npm run dev&lt;/code&gt;, after you deploy your &lt;code&gt;npm run build&lt;/code&gt; code, parts of your code won’t run, even though in development, your DX was great.
With time, you will realize that there is a &lt;code&gt;prerender = true&lt;/code&gt; in some obscure &lt;code&gt;+layout.ts&lt;/code&gt; file somewhere in your hierarchy, and while &lt;code&gt;npm run dev&lt;/code&gt; behaves in one way, thus allowing you to have great DX, the production version behaves in a completely different way.&lt;/p&gt;
&lt;p&gt;You could say that I’m a terrible software developer, a noob with Svelte, and any experienced developer should have known these things.
And by claiming that you will support my point.
DX is great when you begin, even as a newbie.
This is what allows you to move fast.
But when time comes to actually run and &lt;a class=&quot;internal new&quot; href=&quot;/articles/legacy-is-where-companies-go-to-die/&quot;&gt;maintain&lt;/a&gt; your code, you will spend a lot of time trying to understand why this particular piece of code does not execute.
And it’s only after you’ve burned yourself, and now hold this important lesson, and &lt;strong&gt;contextual knowledge&lt;/strong&gt;, in your head—you will be able to move at a decent speed post initial development.&lt;/p&gt;
&lt;p&gt;I had similar experience with NextJS, where everything would work great during development, but in production I’d get hydration errors.
And I bet people have similar experience with Laravel, Django, or Ruby on Rails—all of them claiming to have tremendous DX.
But the moment you need to actually run your code, maintain it, and fix bugs in production, all your DX is thrown away with lack of &lt;a class=&quot;internal new&quot; href=&quot;/articles/it-is-better-to-be-typesafe-than-sorry/&quot;&gt;strong typing&lt;/a&gt;, magic shenanigans, and the need to hold contextual knowledge related to the current minor version of your framework.&lt;/p&gt;
&lt;p&gt;On the other side, however, we have poor DX in the form of Rust.
Jump on the same forums I mentioned early, and you will see all kinds of accusations thrown at Rust: the borrow checker fucks you up, the language is hard to pick up, the &lt;strong&gt;DX&lt;/strong&gt; is poor, etc.
But once you finish your application, not without pain and tears, it will run pretty much forever.
Yes, there could be logical bugs like in any software, but there won’t be surprises or weird behaviors.
What you saw during development, will happen in production.&lt;/p&gt;
&lt;p&gt;Moreover, when you come back to develop yet another feature, or fix yet another bug, there won’t be any surprises or hidden rocks.
Refactoring will be a breeze; reading and navigating the code will make sense; and a chance for you to break something in an unrelated part of the application—is slim.
All this without the need to hold enormous contextual knowledge, and without the fear that the next minor upgrade of your dependency—will spiral your productivity down the abyss.&lt;/p&gt;
&lt;p&gt;Developers are obsessed with tools and frameworks that help them to be more productive.
For years, if not decades, productivity meant speed of writing code, which is important, but it’s a very small part of the entire process of development in general, and if you want to optimize productivity, you better focus on other areas in order to gain more benefits.&lt;/p&gt;</content:encoded></item><item><title>Software Development Fragmentation</title><link>https://kudmitry.com/articles/software-development-fragmentation/</link><guid isPermaLink="true">https://kudmitry.com/articles/software-development-fragmentation/</guid><pubDate>Wed, 30 Oct 2024 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About 15 years ago, I got my first software engineering job, and the way things were done back then—was different.&lt;/p&gt;
&lt;p&gt;I was working in tourism/hospitality, and we were building a B2B product targeted at hotel owners and travel agencies.
The product was big and had vast feature set.
But in addition to the main functionality of the product, we were also building an accounting and BI (business intelligence, a fancy word for analytics), submodules.
We were also building tools that would export our accounting data to standardized industry tools like SAP, but nevertheless this practice of building in-house accounting or analytics is practically non-existent in modern software development.&lt;/p&gt;
&lt;p&gt;If you Google for “&lt;em&gt;how to code an accounting system&lt;/em&gt;”, you will land on a bunch of Reddit or HackerNews posts telling you “&lt;strong&gt;Don’t&lt;/strong&gt;”.
I’m not sure whether this advice stems from the incompetence of the current generation of developers, who are afraid to build anything more complex than saving a few JSONs in a database; or it’s a genuine concern to avoid wasting time on trivial tasks that can be outsourced to a third party SaaS.
Sure, accounting systems are complex, and there are best practices to follow like “&lt;em&gt;don’t use floats for money&lt;/em&gt;” and “&lt;em&gt;please don’t use floats for money&lt;/em&gt;”, but nevertheless they are not impossible to build.&lt;/p&gt;
&lt;p&gt;Despite this fact, today, the consensus seems to be to outsource any mundane task like accounting or analytics to a third party service that is designed for these particular tasks.
Hence, such approach creates a &lt;strong&gt;fragmentation&lt;/strong&gt; in software.
Your product is no longer standalone, and in order to be used properly it needs to be tied to these third party SaaS products.
This, often, leads to absurd scenarios where you collect analytics in tool A, but this tool is so general—in order to appeal to the masses it has to be—that you end up building complex data pipelines to move data from said tool, to &lt;strong&gt;yet another&lt;/strong&gt; tool designed for visualization.
In reality, you have all the data you need, you just need to augment it with some additional analytics collection and build a proper visualization that makes sense &lt;strong&gt;for you&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You might say that it’s a waste of developers’ time and company money to develop yet another analytics tool where there are a dozen existing solutions that cost a cup of coffee and a croissant a month.
But this reminds me of the fallacies described in “Mythical Man Month”.
Sure, you save time on development, but you did not account the time needed for training and mastering said tools.
And let’s be honest: there is no such thing as due-diligence.
You can’t possibly determine whether the tool is right for you by reading their landing page or talking with their sales rep.
The only way to effectively evaluate the tool is by using it.
And by the time you realize that it’s not exactly what you need, you are already knees deep in it, and the sunk cost fallacy starts to kick in, so you overengineer a solution to build a data pipeline to a data warehouse as if you are a warehouseman working in a sorting center at Amazon.
But hey, at least you didn’t have to &lt;em&gt;reinvent the wheel™&lt;/em&gt; and waste company money and resources.
Well done!&lt;/p&gt;
&lt;p&gt;All this could have been avoided if you built a custom tool tailored for your needs.
In the end, you have all the data, why spread it across multiple third party tools in order to later build a data pipeline that unifies it all together?
But no, we need to fragment.
We must not need to reinvent the wheel.
And yes, sometimes it makes total sense.
But I don’t know what is it with the modern software development practices, but we seem to rely too much on third party solutions, as if the role of a software engineer was reduced down to a procurement employee who stitches together a solution based on dozen third party services.&lt;/p&gt;
&lt;p&gt;But you need two for a tango, and as the inefficiencies grow on the development side, so do SaaS companies tend to meet the demand (or maybe they induce it?) to create dull software offerings.
I have yet to see a development company that settled on one particular tool, whether it’s for knowledge sharing, project management, or analytics.
It’s always a combination of multitude of tools because “while Jira works good for us, we actually use Monday for our Gantt progress”; “well, yes, we collect analytics in Google Analytics, but then there is a process that moves data to snowflake to visualize it”, etc.
SaaS models make sense from financial perspective, but they are far from being the most optimal &lt;strong&gt;technical&lt;/strong&gt; solution, as they focus on wider distribution thus offering generalized solutions.&lt;/p&gt;
&lt;p&gt;I’d argue that on-premise/self-hosted tools with access to source code behind a license that forbids redistribution, or dedicated development team, would be a better solution overall.
A company would host the needed tool on their infrastructure—thus removing the need for the tool provider to comply with dozens of regulations (“oh, but we need you to have an EU datacenter because GOVERNMENT”)—while at the same time retaining the ability to modify the tool to suit &lt;strong&gt;their specific&lt;/strong&gt; needs.
In the end, you have a data store, you just need to enhance this data in order to make sense of it &lt;strong&gt;for you&lt;/strong&gt;.
Why would you move analytics data storage to a third party data broker requiring them to comply with whatever regulations you need to comply with, while accepting the risk of data mismanagement, downtime, or being a hostage of their pricing plan—when all this can be achieving by hosting your own, whether purchased or developed in-house, tool?&lt;/p&gt;
&lt;p&gt;I know why.
Because the fragmentation happens not only on the software level, but also on the infrastructure level.
Modern software development is so convoluted, that nobody really knows how the deploy process works or why the fuck it takes &lt;strong&gt;45 minutes&lt;/strong&gt; for your code to reach production.
Asking for a custom deploy solution, outside the established practices by the company, will result in a firm denial from DevOps as their custom terraform wrapper doesn’t know how to deploy Joe Shmoe’s analytics tools.&lt;/p&gt;
&lt;p&gt;So we are left at the mercy of our Lord and Savior the REST API, which allows us to fragment our software further and further, while benefiting no one.
Neither you, whose data is now being spread across N services and requires complex data pipelines, nor the SaaS developers, who try to appeal to the masses by building as abstract as possible—benefit from it.
Really, an analytics tools that shows you how many people are online on your website for $20/m, or “CONTACT US FOR ENTERPRISE” pricing is the best we can do?
Should I remind you that phpBB had this functionality built-in for decades?&lt;/p&gt;</content:encoded></item><item><title>One year of Rust in production</title><link>https://kudmitry.com/articles/one-year-of-rust-in-production/</link><guid isPermaLink="true">https://kudmitry.com/articles/one-year-of-rust-in-production/</guid><pubDate>Sun, 22 Sep 2024 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the past months, I spent (almost) all of my free time working on my side business — &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
It all started with migrations from LemonSqueezy to Stripe.
But like every refactor, or rewrite, it grew way bigger than I anticipated.
A simple payment provider change required me to implement a job processing queue on top of SQL as well as doing a tiny accounting system.
All in Rust of course.
This resulted in one of the biggest merge-requests I have ever merged.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;A merge-request from the new version of JustFax to the master branch&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1162&quot; height=&quot;98&quot; src=&quot;/_astro/rust_pr.Diuc0NQ__Z1VR2tT.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;A merge-request from the new version of JustFax to the master branch&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;This merge-request comes just in time, and reminds me that I have started JustFax about a year ago (maybe 11 months).
In addition to that, I’m working with Rust in production in my &lt;code&gt;$MAIN_JOB&lt;/code&gt;, so I decided to give an overview of what it’s like to use Rust in production.&lt;/p&gt;
&lt;h2 id=&quot;if-it-compiles-it-runs&quot;&gt;If it compiles, it runs&lt;/h2&gt;
&lt;p&gt;I mentioned this briefly in my &lt;a class=&quot;internal new&quot; href=&quot;/articles/building-a-webapp-in-rust/&quot;&gt;initial impression of writing a web application in Rust&lt;/a&gt;, and I want to emphasize this again.&lt;/p&gt;
&lt;p&gt;I don’t, usually, run my code after each line I change (unless I do annoying CSS adjustments).
Most of the time, I finish a big chunk of refactor, or feature implementation, and then run everything to test.
Moreover, with Rust, you can’t run a code that does not compile, so if your code has errors, you need to fix them, unlike JavaScript.
And so I got carried away with the refactor, which span across endless amount of evenings, during multiples months.
One thing led to another, and the code was never in a state when it could compile, until the very last moment when everything was done.&lt;/p&gt;
&lt;p&gt;I was in fear running everything, but after setting up every component of the system, and issuing a first (&lt;strong&gt;local&lt;/strong&gt;) request, I was surprised to see that everything works!
I know that I’m a good developer, but I don’t fool myself that I can write code without mistakes.
And apart from minor issues with dynamic aspects of the code, like wrong serialization/deserialization formats, the code worked flawlessly.&lt;/p&gt;
&lt;p&gt;The first layer for this success, is type-safe, compiled nature of Rust.
You can’t, accidentally, assign a &lt;code&gt;i64&lt;/code&gt; to &lt;code&gt;uuid&lt;/code&gt;, Rust compiler won’t let you do that.
Nor you can access a non-existing field in a struct, unlike JavaScript.
And sure, there are people who claim to never experience &lt;code&gt;cannot access property &quot;foo&quot; of undefined&lt;/code&gt; runtime errors, my experience, as well as the experience of my colleagues — is not the same.
One of the reasons that we use Rust at my &lt;code&gt;$MAIN_JOB&lt;/code&gt;, is because the existing backend, written in dynamic language, became unmanageable.
Sure, you can hold the context of a small application in your head, but at some point it will outgrow your memory.
I remember in the old days when I used to do PHP, at some point we had to resort to dumping the content of the array on screen in order to know what keys it has because some keys would be &lt;code&gt;snake_case&lt;/code&gt;, while other &lt;code&gt;camelCase&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Second layer, and it’s kind of an extension of the first, is the obsession people have with type-safety in Rust community.
And it’s a good obsession, because it gives birth to tools like &lt;a href=&quot;https://github.com/launchbadge/sqlx&quot;&gt;&lt;code&gt;sqlx&lt;/code&gt;&lt;/a&gt; — a compile time, type safe SQL wrapper that runs your queries against a real DB and won’t let your code compile if your query is wrong syntactically, or your try to insert a &lt;code&gt;i32&lt;/code&gt; into a &lt;code&gt;TEXT&lt;/code&gt; column without explicitly casting it.
I used to fear SQL, because it is usually a big part of every application, and it’s easy to get it wrong.
Misspell a query, or mess the arguments, and good luck to you in production.
Many people would write unit tests around SQL queries, while others would resort to using ORMs or code generators from your schema.
But with &lt;code&gt;sqlx&lt;/code&gt; I no longer have to worry.
I get the full power of SQL without going to a high level, very abstracted ORM, or resort to code generation from the database schema.
There are drawbacks, sure, and I’ll mention some of them further in the blog post.&lt;/p&gt;
&lt;p&gt;Another set of tools that I can put this category, are type-safe templating engines like &lt;a href=&quot;https://github.com/djc/askama&quot;&gt;&lt;code&gt;askama&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://maud.lambda.xyz/&quot;&gt;&lt;code&gt;maud&lt;/code&gt;&lt;/a&gt;.
They did not find their place in my project, even though I toyed with them, but they nevertheless remove yet another element of unpredictability from your code.
One of the things that make me chuckle, is when I get an email that starts with &lt;code&gt;Hello {{userName}}&lt;/code&gt;.
It’s funny to get it from a no-name service; it’s embarrassing to get it from a bank.
With type-safe, compiled templates, such errors are non-existent.&lt;/p&gt;
&lt;p&gt;The only thing I miss is a type-safe third-party API interface.
Sure, you can generate an OpenAPI client from an OpenAPI/Swagger spec, but you are still left at the mercy of the third-party API provider, and the fact that they have a spec, does not necessarily mean that they will follow it and won’t break their API, so I’m not sure that we can solve this one.&lt;/p&gt;
&lt;h2 id=&quot;and-when-it-runs-its-very-stable&quot;&gt;And when it runs, it’s very stable&lt;/h2&gt;
&lt;p&gt;I have never ever experienced a Rust process crash.
I did experience a Node process crash.
Unless you pollute your code with &lt;code&gt;.unwrap()&lt;/code&gt; (which basically says “if the result is an error, crash”), there is a high chance your process will never crash.
I have a few &lt;code&gt;.unwrap()&lt;/code&gt; calls in my code, mainly during initialization, so if the config file is missing, or the environment variable was not defined, there is nothing I can do except crash the process.
But in general, Rust requires you to handle errors explicitly.
Everything that returns a &lt;code&gt;Result&amp;#x3C;Something, Error&gt;&lt;/code&gt;, requires you either bubble up the error through the &lt;code&gt;?&lt;/code&gt; operator, or perform a &lt;code&gt;match&lt;/code&gt; statement on it.
In most cases it makes sense.
And in some cases, it’s a bit annoying (for example when you know that the conversion from &lt;code&gt;i64&lt;/code&gt; to &lt;code&gt;i32&lt;/code&gt; will succeed because it holds a number that is well inside the boundaries of &lt;code&gt;i32&lt;/code&gt;, you are still required to do &lt;code&gt;try_from()&lt;/code&gt; which returns a &lt;code&gt;Result&lt;/code&gt;).
But even with the annoying cases, I tend not to resort to &lt;code&gt;.unwrap()&lt;/code&gt;, and would rather log a warning and return some sane default value.
Programming is unpredictable, so at least this way my program can continue to run, while logging the location of where I could have been stupid.&lt;/p&gt;
&lt;h2 id=&quot;and-the-tooling-is-incredible&quot;&gt;And the tooling is incredible&lt;/h2&gt;
&lt;p&gt;JustFax is not just Rust, it also has internal systems written in TypeScript, and other JS frameworks.
And I swear that every time I create a new TypeScript project, something changes.
Either the config file changed for some tool (looking at you &lt;code&gt;eslint&lt;/code&gt;); or there is new boilerplate for typescript; or &lt;code&gt;express&lt;/code&gt; is no longer cool and now everyone uses &lt;code&gt;fastify&lt;/code&gt;; or &lt;code&gt;express&lt;/code&gt; is &lt;a href=&quot;https://github.com/expressjs/express/blob/5.0/History.md&quot;&gt;cool again&lt;/a&gt;; or &lt;code&gt;ts-node&lt;/code&gt; is bad, now we use &lt;code&gt;tsx&lt;/code&gt;; or this module is &lt;code&gt;esm&lt;/code&gt; only, while this one is &lt;code&gt;cjs&lt;/code&gt; so go figure.
There is always something with TypeScript.
The way you work, the way you lint your code, the way workspaces work in vanilla JS compared to &lt;a class=&quot;internal new&quot; href=&quot;/articles/npm-workspaces/&quot;&gt;TypeScript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But not with Rust.
&lt;code&gt;cargo init&lt;/code&gt; - boom you get a new project.
Want workspaces?
No problem, works flawlessly.
Linting?
&lt;code&gt;clippy&lt;/code&gt; got you covered.
It’s just so much less boilerplate and mental fatigue.&lt;/p&gt;
&lt;h2 id=&quot;compile-time-is-still-pita&quot;&gt;Compile time is still PITA&lt;/h2&gt;
&lt;p&gt;Undoubtedly the biggest drawback of Rust — is its compile time.
Especially when you resort to using tools like &lt;code&gt;sqlx&lt;/code&gt; or &lt;code&gt;maud&lt;/code&gt; which rely heavily on macros, both the LSP during development, and the compiler — start to struggle.
As the project grows, I collect more third-party dependencies.
And while I try to optimize them by putting common dependencies between multiple packages, in the root &lt;code&gt;Cargo.toml&lt;/code&gt;, as well as aggressively selecting only the needed dependency features, I still struggle with compile time.
I mentioned how the initial compilation took about 6 minutes inside a CI/CD, which now takes about 20 minutes.
It’s possible to optimize it according to different blogs online, but it requires a surgical-like procedure to aggressively cache the dependencies in a multistaged docker container.
I don’t have time for that right now, and need to focus more on the business side, but I will get to it later.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;It has been pointed out to me, in a &lt;a href=&quot;https://www.reddit.com/r/programming/comments/1fmq302/comment/lod53b3/&quot;&gt;Reddit discussion&lt;/a&gt; about this post, that I use a wrong cache key for CI.
Fixing this, should fix the long CI/CD pipeline, because then the build will use previously built/compiled artifacts.
I haven’t tried it yet, due to lack of time.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Local compilation on a Mac M2 is manageable especially with incremental build, but it comes at a price of storage.
Once in a while I run &lt;code&gt;cargo clean&lt;/code&gt;, which results in tens of GBs of cache being removed.
However, even with incremental compilation, it’s still far from instant hot reloading that you get in JS world.
So circling back to my first point, a development cycle of “change small piece of code, test immediately” kind of breaks.
You don’t get this instant feedback that you get with JS/TS, and that’s the reason some of the internal, and external, tools for JustFax are still written in TypeScript.
Rust kind of guides you towards “write a lot of code, compile, check” flow, rather than the “change one line, alt-tab browser” workflow that you have with Node/JavaScript.
I’m mostly fine with that.
I get a lot in return, like real type-safety.
The CI pipeline, I believe, can be fixed, I just need more time to work on it.&lt;/p&gt;
&lt;h2 id=&quot;the-tools-are-not-quite-the-same-yet&quot;&gt;The tools are not quite the same, yet&lt;/h2&gt;
&lt;p&gt;There are things that Rust excel at, such as pure backend.
Frameworks like &lt;code&gt;axum&lt;/code&gt; provide a lot of needed parts to build an API server, and most (popular) external APIs have a client for their API in Rust.
Sure, they are usually not maintained by the company who builds these APIs, but at least we can use them.
However, most examples on the web do not include Rust.
And so you are left on your own to figure out how to integrate with this or that API.&lt;/p&gt;
&lt;p&gt;And it’s a recurring theme with development in Rust, at least for web, I can’t comment on other types of applications.
I often find myself reading the source code, or navigating the GitHub issues for similar problems to the one I encounter.
LLMs rarely help with a proper solution, as most of the packages are kind of niche.
Rust community has great attention to documentations and examples for their packages, which is awesome, but inevitably you will end up with some edge case that is neither documented, nor has an example for it, and chances are you will have to resort to reading the source code in order to figure stuff out.&lt;/p&gt;
&lt;p&gt;And some applications are not well suited for Rust, especially if you come from fast-prototyping environments.
I still prefer to write frontend in TypeScript using Astro or Svelte.
Rust DX is just not great in this department.
With the need to recompile the code upon every variable change, it makes it too slow to iterate fast on frontend development.&lt;/p&gt;
&lt;p&gt;In conclusion, I’m happy that I have chosen Rust one year ago.
Not only it helped me secure a &lt;code&gt;$MAIN_JOB&lt;/code&gt; that I really like, but it also helped me to build better software.
And I’m looking forward for the second year of developing Rust.&lt;/p&gt;</content:encoded></item><item><title>Software development is hard</title><link>https://kudmitry.com/articles/software-development-is-hard/</link><guid isPermaLink="true">https://kudmitry.com/articles/software-development-is-hard/</guid><pubDate>Sat, 17 Aug 2024 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my &lt;a class=&quot;internal new&quot; href=&quot;/articles/lets-blame-the-dev-who-pressed-deploy/&quot;&gt;latest blog post&lt;/a&gt;, I compared software engineers to construction engineers.
I said, that if we, software engineers, want to have the same level of responsibility as other engineers, we need to have the same level of respect other engineers get.
But something was haunting me.
I was thinking a lot about software engineering for the past month, and I couldn’t understand why software engineers are different.
Could it be “just because”?
Software is young, compared to other engineering professions.
Could it be that we, as humanity, haven’t really understood software development to it’s fullest in order to hold software engineer with the same level of responsibility as we hold other engineers?
But then, it all came to me.&lt;/p&gt;
&lt;p&gt;I was writing code that integrates with another third-party API.
As usual, I was consulting the API documentation.
There were no official libraries, and I had to construct the API client using the documentation and trial and error.
All went well, I did some testing, and eventually pushed the code to production.
Sometime afterward, the code broke.
My server would return 500 error code.
Why?
Because the API provider changes their API.&lt;/p&gt;
&lt;p&gt;Who is at fault?
Me, for not monitoring the API change logs?
The API provider for not communicating these changes to all its consumers?
Is it even possible?
An average website communicates with a dozen of third party providers.
We don’t think about it, and take a lot of these dependencies for granted.&lt;/p&gt;
&lt;p&gt;Let’s look at a database for example.
Nearly every web service, except for statically rendered web pages, depend on a database.
Most of us take it for granted.
Have you ever checked that your &lt;code&gt;UPDATE table_name SET field = ? WHERE id = ?&lt;/code&gt; actually affect any rows?
What if it doesn’t?
What do you do?
And what happens when &lt;code&gt;pool.acquite()&lt;/code&gt; fails to acquire a connection from the connection pool?
Sure, it almost never happens, but a sporadic network error could cause your code to fail.
But none of us think of such cases.&lt;/p&gt;
&lt;p&gt;Let’s look at another example: writing a file to the disk.
During our day-to-day life, we rarely encounter errors when trying to write a file to the disk.
But it can happen.
A bug in the OS.
Lack of disk space.
Absence of writing permission for the process.&lt;/p&gt;
&lt;p&gt;We take all these things for granted, as if they can’t fail, but they can, and they do.
And then the question raises: how can we demand accountability from software engineers when they depend on systems they did not develop nor understand fully?
An electrical engineer checks the wiring in your home, he is not responsible for the grid providing you 500 volts instead of 220 volts.
When we develop code, we make assumptions.
Assumptions that the network will function, because if it’s not, the IT engineer needs to fix it.
Assumptions that the disk is not full, because we, hopefully, have monitoring alerts on disk size.
And worse of all, we integrate with a dozen of providers, often times using lousy documentation in combination with trial-and-error or some test data, to determine the happy path flows.&lt;/p&gt;
&lt;p&gt;It might sound like I’m whining.
Like I’m trying to say “&lt;em&gt;Hey, we are poor software engineers, don’t demand accountability from us!&lt;/em&gt;”.
But I’m not.
I truly think that software development is &lt;strong&gt;hard&lt;/strong&gt;.
Sure, it might sound like we put stuff in a database or move pixels on a screen, but it requires so much effort, and belief in others, that I don’t think it’s even possible to develop a fault-tolerant software.
We can reduce the amount of bugs by, say, doing more tests, but PMs don’t have time for tests.
Great software exist, but it comes with a cost.
A deadly cost of time.
When you don’t have the luxury of time, you end up with “&lt;em&gt;Ops, something went wrong&lt;/em&gt;” type of errors.&lt;/p&gt;
&lt;p&gt;Software development depends on a lot of verbal agreements, that rarely are respected.
It depends on a lot of assumptions.
Assumptions that the network will be there, that the database will be able to update the record, and that the user won’t take the hard drive out of the computer while running our software.
Most of these assumptions are legit.
That’s why most of the software works most of the time.
But what do we do when we can’t acquire a database connection because the machine has reached its memory limit?
Do we retry again?
For how long?&lt;/p&gt;
&lt;p&gt;Software development is &lt;strong&gt;hard&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>On build in public and indie hacking communities</title><link>https://kudmitry.com/articles/on-buildinpublic-and-indiehackers/</link><guid isPermaLink="true">https://kudmitry.com/articles/on-buildinpublic-and-indiehackers/</guid><pubDate>Wed, 14 Aug 2024 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After failing my first business idea (some context in available in &lt;a class=&quot;internal new&quot; href=&quot;/articles/6-lessons-6-months-6-projects/&quot;&gt;this blog post&lt;/a&gt;), I stumbled upon two communities: the &lt;em&gt;#buildinpublic&lt;/em&gt; and the indie hackers community.
In this post, I want to share my experience about these two communities.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I have no intention to discredit or offend any particular individual or groups of people.
My desire is only to share my findings about the various communities online for solopreneur/entrepreneurs.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I tried to understand what failed with my first business idea, and what I can improve.
Moreover, being a digital nomad back then, I wanted to connect with some other solopreneurs and learn together.
This is how I came across two different, but very similar, communities online: &lt;em&gt;#buildinpublic&lt;/em&gt; and the indie hackers community.&lt;/p&gt;
&lt;h2 id=&quot;what-are-they&quot;&gt;What are they?&lt;/h2&gt;
&lt;p&gt;Hashtag build-in-public is not a community per se, but rather a movement.
You share your entrepreneurship story and tag it with &lt;em&gt;#buildinpublic&lt;/em&gt; tag in various social networks.
This allows fellow community members learn from your experience and mistakes, as well as share theirs.
The hashtag itself more or less dead on Twitter, since Twitter killed hashtags all together, but a community named &lt;em&gt;#buildinpublic&lt;/em&gt; still exists.&lt;/p&gt;
&lt;p&gt;Indie-hackers is yet another community of builders who are independent.
It’s not limited to entrepreneurship and there are indies in every industry: indie game developers, indie artists, indie musicians etc.
Indie hackers, on the other hand, is a community focused more on building side-projects / businesses, and a website named &lt;a href=&quot;https://www.indiehackers.com/&quot;&gt;Indie hackers&lt;/a&gt; serves as yet another platform for indie hackers to connect.
Many people who build solo, without any external funding or raising money, would often refer to themselves as “indie hackers”.&lt;/p&gt;
&lt;h2 id=&quot;my-experience&quot;&gt;My experience&lt;/h2&gt;
&lt;p&gt;I don’t remember how I was exposed to the indie hackers / build in public communities, but I guess after doing some reading online, I came across a community of people, mainly on Twitter, that share their knowledge and experience there (at least that’s what I thought they do).
So I revived my Twitter account, and started to post my journey.
I had about 40 followers, and today I have a bit more than 500.
I’ve been posting for more than a year now, almost daily.
The amount on knowledge I got from Twitter is close to zero.&lt;/p&gt;
&lt;p&gt;Sure, I was able to harvest gems of information here and there, but overall I had to navigate a bunch of attention seeking, repetitive, and irrelevant posts.
It’s equivalent to garbage diving — you can, rarely, find a thousand-dollar in garbage bins, but you have to go through a lot of garbage.
Most people on Twitter want to sell you their stuff, or post very basic stuff like “&lt;em&gt;what framework do I chose for my Instagram killer&lt;/em&gt;”.
I had a conversation with my friend, who also tries to build a business, and interestingly enough, he came to the same conclusion.
None of these so-called communities, are actually communities — they are more like support groups for people who don’t really want to do the job.&lt;/p&gt;
&lt;p&gt;You see, entrepreneurship requires many skills.
One of these skills is being able to do proper research.
This includes actually searching for information, as well as asking proper questions when you fail to find the required information.
But most of the posts in these “entrepreneurship” communities are low effort posts the answer to which could be found in Google/ChatGPT in 2 minutes.
“What business do I start”, “how do I collect payments”, “what framework to chose”, etc.
You can’t expect to be spoon-fed and be an entrepreneur.
The internet is full of information, both free and paid, where you can learn many aspects of starting, and running a business.
And in the end, you need to make some moves, even if these moves result in failures.&lt;/p&gt;
&lt;p&gt;I recently quit all social media, cold-turkey.
There are many reasons, some of them are related to the nature of social media (I posted about it in my other blog &lt;a href=&quot;https://jikokaizen.blog/post/the-downside-of-social-media/&quot;&gt;link here&lt;/a&gt;), but as it relates to entrepreneurship, I just got sick of this “community”.
I was afraid to do it, because I was thinking that I would be missing on great ideas, but after weeks of seeing the same content over and over again, I decided that I need to stop wasting time there.&lt;/p&gt;
&lt;p&gt;It reached absurd levels where people would post how they bought an air fryer, because another well known indie-hackers bought an air fryer, and how that made them “indie hackers”.
People would spend money buying courses and books on marketing, rather than experimenting with ads or SEO.
Of course there are people who use their audience to promote their products, and if that works for you, then go ahead.
But in my case, my audience is not indie-hackers on Twitter, and from my experience, indie-hackers would rather build their own clone of your tool than pay for an existing solution.&lt;/p&gt;
&lt;p&gt;The term “community” was ruined by social networks.
And for me, I didn’t see any benefits after being active a year on Twitter, from being in such “community”.
Jaen posted this a while ago, and I think it summarizes the indie-hacking community very well:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Starting to identify less and less as an &quot;Indie Hacker&quot; over time.&lt;br&gt;​&lt;br&gt;Feels like these days a lot of Indie hackers:&lt;br&gt;- complain about &quot;copycats&quot;&lt;br&gt;- build SaaS starter kits or other useless products for other indies&lt;br&gt;- spend more time on x talking about building than actually building… &lt;a href=&quot;https://t.co/rIbLntkHvt&quot;&gt;pic.twitter.com/rIbLntkHvt&lt;/a&gt;&lt;/p&gt;— Jáen ff/sr (@speedrunjaen) &lt;a href=&quot;https://twitter.com/speedrunjaen/status/1816639620265742563?ref_src=twsrc%5Etfw&quot;&gt;July 26, 2024&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;I can’t speak about the B2B part, but the “virgin indie hacker” is pretty much accurate.
Very few people in the indie-hacking community actually build one product and stick with it.
Many people jump from product to product, trying to reach the pedestal of “&lt;em&gt;I built 459 projects that failed before finding the one that succeeded&lt;/em&gt;”.
They don’t share any insights or knowledge, because they are too afraid to experiment and would rather rewrite their “business” from Ruby to Node, to Laravel, to Next.js, etc.&lt;/p&gt;
&lt;p&gt;Some people do benefit from social network presence, mainly people who sell information or shortcuts.
But I’m not in the market of selling information or code boilerplate, so I can’t seem to justify being present there anymore.&lt;/p&gt;
&lt;p&gt;Few weeks ago, I spend a weekend in Belgium with the co-founders of the startup I work at.
I spoke for about 3 hours with one of them.
A very interesting conversation on society, community, and life in general.
When we discussed how strange the current society is, he said something that stuck with me.
I had the same feeling for a long time, but couldn’t express it in words, but he did.
He said: “&lt;em&gt;I no longer participate in society. I’m there, I do what everyone else are doing, like buying groceries, but I don’t participate in this madness anymore.&lt;/em&gt;”&lt;/p&gt;
&lt;p&gt;This phrase summarizes well my current position on social networks.
I will continue to use them as a vessel to spread my knowledge (such as my blog posts), and maybe occasional short posts of wisdom, but I’m not going to participate in this charade any more.
I think there is a reason why people like &lt;a href=&quot;https://sive.rs/about&quot;&gt;Derek Sivers&lt;/a&gt;, Cal Newport, and many people from Newports book “Deep Work” — don’t keep any social networks, and communicate strictly via email.&lt;/p&gt;
&lt;p&gt;My email is available in many places, so feel free to reach out.
Oh, and if you know any online communities, or in-person ones in The Netherlands, that are focused on &lt;strong&gt;real&lt;/strong&gt; entrepreneurship, please let me know, I’ll be grateful.&lt;/p&gt;</content:encoded></item><item><title>Let&apos;s blame the dev who pressed &quot;Deploy&quot;</title><link>https://kudmitry.com/articles/lets-blame-the-dev-who-pressed-deploy/</link><guid isPermaLink="true">https://kudmitry.com/articles/lets-blame-the-dev-who-pressed-deploy/</guid><pubDate>Sun, 21 Jul 2024 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday I was browsing &lt;code&gt;/r/programming&lt;/code&gt; and came across a blog post titled &lt;a href=&quot;https://mccue.dev/pages/7-20-24-programmers-deserve-consequences&quot;&gt;&lt;em&gt;“After CrowdStrike, Programmers Deserve Consequences.”&lt;/em&gt;&lt;/a&gt;
The author opens up with a comparison of Software Engineers to an Anesthesiologist or a Structural Engineer, and tells a personal story about his friend who got stuck in a hospital due to the &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-day-of-the-bsods/&quot;&gt;CrowdStrike outage&lt;/a&gt;.
And it’s true—software bugs can kill people.
Mistakes in airplane autopilot software, or malfunction in equipment run by software—can cost the lives of people.&lt;/p&gt;
&lt;p&gt;But then the author engages in an absurd rant about how the entire software engineering industry is a “bit of a clusterfuck”, and makes a delusional claim how software engineers should bear the responsibility for bugs and outages.
It’s not the first time I hear these arguments.
I remember when &lt;a href=&quot;https://en.wikipedia.org/wiki/Volkswagen_emissions_scandal&quot;&gt;VW emission scandal&lt;/a&gt; was revealed, people had the same arguments: the developer(s) who coded the bypass—should be responsible.
I’ll explain, in the end of the article, why I don’t think developers should be responsible, but for now, let me give you some more figures to blame.&lt;/p&gt;
&lt;p&gt;We can start with &lt;strong&gt;the CEO&lt;/strong&gt;.
When you argue with CEOs why they deserve such a big pay and bonuses, they will usually say something along the lines of: &lt;em&gt;“Entrepreneurship implies huge risk and lays the responsibility for failure on the shoulders of the founder/CEO”&lt;/em&gt;.
And it’s true.
Founders/Entrepreneurs bear a lot of risk.
They go on raising money, getting into debt, take the role of running a company and they, usually, enjoy the result of their success—which is translated to a nice salary—or bear the consequences of failure.
At least they used to.&lt;/p&gt;
&lt;p&gt;I remember times when leaders had dignity and self-respect.
They would go on stage and apologize.
They would take responsibility and outline an action plan.
Some even stepped down from their position as a sign of failed management.
But today?
Today, they hide behind an army of PR people who write blunt, soulless, unsympathetic press releases, blame their customers and compare their employees to a number in a balance sheet.
They no longer care about serving the public, but rather generating profit for investors.&lt;/p&gt;
&lt;p&gt;And, usually, they fail upwards.
George Kurtz, the CEO of CrowdStrike, used to be a CTO at McAfee, back in 2010 when McAfee had a similar global outage.
But McAfee was bought by Intel a few months later, Kurtz left McAfee and founded CrowdStrike.
I guess for C-suite, a global boo-boo means promotion.&lt;/p&gt;
&lt;p&gt;But if C-suite fail upwards, then the next obvious party to blame is &lt;strong&gt;the Customer&lt;/strong&gt;.
We could blame United or Delta that decided to run EDR software on a machine that was supposed to display flight details at a check-in counter.
Sure, it makes sense to run EDR on a mission-critical machine, but on a dumb display of information?
Or maybe let’s blame the hospital.
Why would they run EDR on an MRI Machine?&lt;/p&gt;
&lt;p&gt;We should blame the IT department in these companies.
Unless we understand that the IT department was simply trying to check a box in a stupid regulation imposed by the government.
So it makes sense to blame &lt;strong&gt;the Government&lt;/strong&gt;.
A government that creates absurd regulations by senile people whose familiarity with technology is limited to their TV remote.
Have you seen the absurd questions by the US senate to tech entrepreneurs?&lt;/p&gt;
&lt;p&gt;Politicians are good at what they do—creating regulations.
The companies are then left at the mercy of implementing these regulations.
I imagine an IT specialist trying to explain to his manager how it makes no sense to install EDR software on an MRI machine, but his manager has no clue about it and simply tries to follow the regulation.
So maybe we should blame the &lt;strong&gt;middle management&lt;/strong&gt;?
The same middle management who cuts corners because they need to demonstrate KPIs, or fulfill unrealistic deadlines that they, themselves, created?&lt;/p&gt;
&lt;p&gt;Or, we cane go a level higher and blame the &lt;strong&gt;board&lt;/strong&gt;.
The board who cares only about one thing: $$$.
How can we save dollars?
Let’s fire, sorry &lt;em&gt;layoff&lt;/em&gt;, some engineers.
Clearly, these overpaid peasants don’t deserve the money they are getting paid, and we can remove some of them.
Or better, replace them with AI.
I won’t be surprised if this outage was caused by CorwdStrike trying to “optimize” it’s pipeline by &lt;del&gt;firing&lt;/del&gt; laying off some QA engineers and or &lt;a href=&quot;https://www.techradar.com/pro/nvidia-ceo-predicts-the-death-of-coding-jensen-huang-says-ai-will-do-the-work-so-kids-dont-need-to-learn&quot;&gt;replacing them with AI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But the truth is, we can’t blame any of the above.
They are the &lt;em&gt;innovators&lt;/em&gt;, the &lt;em&gt;entrepreneurs&lt;/em&gt;, the &lt;em&gt;backbone of our economic systems&lt;/em&gt;.
They all fail upwards.
The white knights of our modern society.
Smartest, most successful individuals whose words and practices should be taken without questions as if they spread the message of God himself.&lt;/p&gt;
&lt;p&gt;No, we can’t blame them.
Instead, we should blame the peasants.
These dirty developers who make a lot of, unjustified, money, whose work can be replaced by AI.&lt;/p&gt;
&lt;p&gt;You want to gate keep the word “Engineer”?
You want to compare Software Engineers to Structural Engineers?
Then I have a shocking discovery for your.
A Structural Engineer won’t sign off a design that was made under pressure or with cut corners.
He will shift the blame to the construction if they decide to use a different type of steel or cheaper screws.
And if you decide to replace an Anesthesiologist with AI, then good luck to you.&lt;/p&gt;
&lt;p&gt;The reason why Anesthesiologists or Structural Engineers can take responsibility for their work, is because they get the respect they deserve.
You want software engineers to be accountable for their code, then give them the respect they deserve.
If a software engineer tells you that this code needs to be 100% test covered, that AI won’t replace them, and that they need 3 months of development—then you better shut the fuck up and let them do their job.
And if you don’t, then take the blame for you greedy nature and broken organizational practices.&lt;/p&gt;
&lt;p&gt;Because blaming software engineers is nothing more than satisfying the bloodthirsty public for your organizational malpractices.
Sure, you will get the public what they want, but you won’t solve the root cause of the problem—which is a broken pipeline of regulations by people who have no idea what are they talking about, to CEOs who are accountable only to the board of directors, to upper and middle management who thinks they know better and gives zero respect to the people who actually do the work, while most of the latter just want to work in a stable environment where they are respected for their craft.&lt;/p&gt;</content:encoded></item><item><title>The day of the blue screens of death</title><link>https://kudmitry.com/articles/the-day-of-the-bsods/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-day-of-the-bsods/</guid><pubDate>Fri, 19 Jul 2024 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier today, a broken update from &lt;a href=&quot;https://www.crowdstrike.com&quot;&gt;CrowdStrike&lt;/a&gt; caused massive global outages in machines that are running Windows.
A component named “Falcon”, that is responsible for endpoint protection, and runs in kernel-mode, causes machines to fail into blue screen of death (BSOD) and enter a boot loop.
Among the industries that were affected are: airlines and airports, railways, healthcare, postal services, broadcasting, retails shops, banks, stock exchanges, etc.&lt;/p&gt;
&lt;p&gt;While I could talk about the reason behind this outage, smarter people than me already analyzed it, so go read their reviews if you are interested.
Instead, I want to talk about how we got there.&lt;/p&gt;
&lt;p&gt;When you zoom out, and look at the situation from above, you realize two important facts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A lot of critical infrastructure is, still, running on Windows&lt;/li&gt;
&lt;li&gt;Windows is not secure enough&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I haven’t used Windows in over a decade now.
I first switched to Linux, and after getting a Mac from my former employee, I made a switch to macOS.
Occasionally, I run Windows in an isolated VM due to development that I have to test on Windows.
Back in the days I used to run Windows, I remember that the first thing I had to install on a fresh Windows machine — is an antivirus.
Modern Windows still feels like a malware.
It’s sad that Microsoft decided to focus on putting ads in their system, rather than making their system more secure by default.
Instead, you have to reach to third party providers to protect your systems.&lt;/p&gt;
&lt;p&gt;More over, these third party providers operate in a push manner, where updates are distributed automatically without any approval from the system administrator.
In Linux based servers and systems, updates are, usually, performed in a pull manner, where the DevOps engineers or the system administrator goes and updates the system, and it’s underlying components.
And the result of a push based system is evident in today’s outage.
A private company that has access to most critical civilian infrastructures and can bring to an economical disaster.
Hell, forget economy, I’ve read comments on HackerNews about hospitals going down during &lt;a href=&quot;https://news.ycombinator.com/item?id=41002977&quot;&gt;&lt;strong&gt;heart attack surgeries&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I could rant about how irresponsible CrowdStrike was during the rollout of this update.
How they should have better rollout mechanisms, or how we should find the one’s responsible and prosecute them.
But I hate it when after every incident people jump into &lt;em&gt;“we need to find someone to blame/take responsibility”&lt;/em&gt; mantra.
The sad reality is that nothing will happen, and no one will take responsibility.
Instead, I want to talk about something that no one mentioned that much, and it’s the &lt;strong&gt;security through obscurity&lt;/strong&gt; that many companies practice.&lt;/p&gt;
&lt;p&gt;In order to understand, and prevent such cases in the future, we need to talk about why CrowdStrike is so popular.
You see, as companies become bigger they start to drown in bureaucracy.
Some of it is self-inflicted, while other is imposed by the government/regulating entity.
To put it bluntly: companies need to check boxes.&lt;/p&gt;
&lt;p&gt;The intention of “box checking” is good: we want to prevent outages and security incidents.
But at some point, this ritual becomes so engraved in the minds of the developers and the management, that nobody dares to challenge it anymore.
&lt;em&gt;“We need to run security scans.”&lt;/em&gt;
Why?
Nobody knows, we just need in order to pass some audit.
And sure, at the beginning you try your best to fix all vulnerabilities found by the scans.
But then you meet a stubborn person, to whom you try to explain that this particular vulnerability can not be exploited because the package that is responsible for this vulnerability is used only during testing phase, and is removed from the final build.
But someone needs to tick a box somewhere, and so you sweat and pull out your hair in a desperate attempt to fix this vulnerability, and eventually you end up with some hack, just to satisfy the scanner and your boss/auditor.&lt;/p&gt;
&lt;p&gt;And sure, this example can be somewhat imaginary, but not far from the truth.
Everyone who worked in medium to big sized corporate, have encountered horror stories about security through obscurity.
A ritual, or a workflow that exist for the sake of a checkbox on some audit paper, but serves no real purpose in strengthening the security of the company and or the product.
And so we end up outsourcing our security to third party companies who scare you with how 62 minutes of downtime can kill your business, and instead you should use their software.
A sales rep that convinces a higher up manager, who convinces everyone else that we must use it for our own security.
And there is no sane person in this chain to question the absurdity of the decision and the potential dangers it might inflict to your business, because they tell you: “we can’t fail”.&lt;/p&gt;
&lt;p&gt;But the truth is: everyone can fail.
Companies are made of people, and people make mistakes.
The question is how you recover from it.
And in the case of this outage, the recovery is very painful.
It’s a long weekend where system administrators will need to recover &lt;strong&gt;every, single, machine&lt;/strong&gt;.
I don’t envy them.
&lt;em&gt;“Somebody should take responsibility.”&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>What they do (not) teach you about Entrepreneurship in school</title><link>https://kudmitry.com/articles/what-they-do-not-teach-you-about-entrepreneurship-in-school/</link><guid isPermaLink="true">https://kudmitry.com/articles/what-they-do-not-teach-you-about-entrepreneurship-in-school/</guid><pubDate>Fri, 12 Jul 2024 14:30:00 GMT</pubDate><content:encoded>&lt;p&gt;2008 was my final high school year.
Closer to the end of the year, almost after all the final exams, the school didn’t really know what to do with us.
So they decided to instill entrepreneurship ideas in the minds of the pupils.
Being a school with a strong accent on mathematics and computers, they decided to invite some tech entrepreneurs.
In a somewhat voluntarily, 1-hour session, inside the school auditorium—two people sat on a stage side by side.&lt;/p&gt;
&lt;p&gt;To my left, sat &lt;a href=&quot;https://en.wikipedia.org/wiki/Yossi_Vardi&quot;&gt;Yossi Vardi&lt;/a&gt;.
Yossi Vardi is the father of, and the investor in, Arik Vardi—one of the co-founders of &lt;a href=&quot;https://en.wikipedia.org/wiki/ICQ&quot;&gt;ICQ&lt;/a&gt;.
He is a serial tech entrepreneur who invested in more than 85 high-tech startups, and I can’t find a reliable source for his net-worth, but ICQ alone has been sold to AOL for $287 millions in cash back in 1998 (that’s more than half a billion today, adjusted for inflation).&lt;/p&gt;
&lt;p&gt;To my right, sat Alex Sirota.
Alex is not as known as Yossi, but he founded a browser extension named &lt;a href=&quot;https://en.wikipedia.org/wiki/FoxyTunes&quot;&gt;FoxyTunes&lt;/a&gt;—in which Yossi Vardi invested—and later sold it to Yahoo (yes, that &lt;a href=&quot;https://www.yahoo.com/&quot;&gt;Yahoo&lt;/a&gt;) for $30 millions.&lt;/p&gt;
&lt;p&gt;They kept on talking about entrepreneurship, the importance of believing in yourself, etc.
I don’t remember the entire content of the session, but I remember how I felt afterward.
I felt that I’m going to be a tech entrepreneur!
This was such a strong feeling.
I remember that I was dating a girl back then, and she (together with her girlfriends) decided to “skip” this “non-mandatory” session and go home.
When we met later that day, she asked me what was the session about, and I did the most businessman face I could, and I answered her &lt;em&gt;“We spoke about tech entrepreneurship”&lt;/em&gt;, like I was some important VC, and we just had a board meeting.&lt;/p&gt;
&lt;p&gt;I spend the summer break trying to build a link sharing services, similar to &lt;a href=&quot;https://digg.com/&quot;&gt;digg.com&lt;/a&gt;.
I’m going to be a tech entrepreneur in the golden years of the Internet.
But I failed.
I though maybe it’s not for me, maybe I should focus more on college.
But then came something big.&lt;/p&gt;
&lt;h2 id=&quot;the-social-network&quot;&gt;The Social Network&lt;/h2&gt;
&lt;p&gt;Remember the movie “The Social Network” that came out in 2010, where Jesse Eisenberg plays Mark Zuckerberg in a story of founding of &lt;del&gt;The&lt;/del&gt; Facebook?
How he came up with the idea?
The two nasty lawsuits between the Winkelvoss twins, and his friend Eduardo Saverin?
The saga with Sean Parker, Napster and the “cool billion-dollar company”?&lt;/p&gt;
&lt;p&gt;An inspiring piece of cinematography.
I re-watched it recently.
A story that will truly spark entrepreneurship in every one of us.
All you need is an idea and a laptop, the true dream of tech entrepreneurship.&lt;/p&gt;
&lt;p&gt;This movie signaled a new era, the era of Tech Entrepreneurs.
Movie after movie, book after book, we got a glimpse into the world of big tech entrepreneurship.
Companies like Uber, Airbnb, Instagram, WhatsApp, Dropbox, Spotify, and more—became big during that period.
Names like Mark Zuckerberg, Elon Musk, Jack Dorsey, Drew Houston, and more—resembled the typical image of a tech entrepreneur.
And this reignited my desire to be like one of them.&lt;/p&gt;
&lt;p&gt;Except for one little thing: &lt;strong&gt;it’s all a lie&lt;/strong&gt;.
Sort of.&lt;/p&gt;
&lt;h2 id=&quot;the-truth&quot;&gt;The truth&lt;/h2&gt;
&lt;p&gt;They say that school does not prepare you for the real world.
Nor does Hollywood.
Dreaming big is important, but your chances of being the next Mark Zuckerberg, or the next Elon Musk, are slim.
Approaching zero I’d say.
Movies rarely capture the true story, and they tend to omit details in the backstory of the founders.
They are an inspiring story of tech geeks who just messed with computers and “accidentally” built a billion-dollar company.
They don’t talk about the struggles of raising money, and they ignore the thousands, if not more, of people who tried and failed.&lt;/p&gt;
&lt;p&gt;What they do, however, apart from presenting an interesting story, is instilling a false belief.
The false belief of &lt;strong&gt;“Go big, or go home.”&lt;/strong&gt;
They promote the idea that successful entrepreneurship, especially in tech, is when you raise millions of dollars in capital, and reach a $1B valuation.
Which in reality is extremely hard, and never achieved by one person (despite what the movies try to tell you).&lt;/p&gt;
&lt;p&gt;But there are many other entrepreneurs.
Some of them work in small teams, others prefer to work alone—thus known as &lt;strong&gt;solopreneurs&lt;/strong&gt;.
Some raise money, while others bootstrap their business alongside their 9-5 job.
Their revenue varies: some make millions, while others have enough to live comfortably.
Many of them also fail.
In the end, entrepreneurship is hard, and it’s not for everyone.&lt;/p&gt;
&lt;p&gt;And yet, they rarely present at panels in schools, or have movies filmed based on their biography.
But this lifestyle has way higher chance for success than thinking that you can be the next Mark Zuckerberg.
It took me 14 years to change my mind and instead of focusing on a billion-dollar idea, focus on building small, sustainable online business.
And it’s only when I left my job, a bit more than one year ago, and decided to travel for some time while building an online business, I’ve realized that maybe that’s what we should teach kids in schools if we want to instill entrepreneurship in them.&lt;/p&gt;</content:encoded></item><item><title>One year of solopreneurship</title><link>https://kudmitry.com/articles/one-year-of-solopreneurship/</link><guid isPermaLink="true">https://kudmitry.com/articles/one-year-of-solopreneurship/</guid><pubDate>Fri, 05 Jul 2024 19:40:00 GMT</pubDate><content:encoded>&lt;p&gt;A little bit more than a year ago, I quit my job due to &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;burnout&lt;/a&gt;.
I, then, embarked on a voyage in Central America with a simple goal in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Focus on my mental health&lt;/li&gt;
&lt;li&gt;Focus on my physical health&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build an online business&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Little I knew that starting an online business is not as easy as it seems.&lt;/p&gt;
&lt;h2 id=&quot;a-quick-recap&quot;&gt;A quick recap&lt;/h2&gt;
&lt;p&gt;I did a 6-month overview &lt;a class=&quot;internal new&quot; href=&quot;/articles/6-lessons-6-months-6-projects/&quot;&gt;here&lt;/a&gt;, so you can go read it as well, but here is a quick recap.
I built 6 different products while nomading for 6 months in Central America.
By the end of 2023, I moved permanently to The Netherlands and got a full time job.
Few weeks ago, I started to get &lt;em&gt;domain expiration&lt;/em&gt; emails from Namecheap.
The same domains that I bought about a year ago for my first projects.&lt;/p&gt;
&lt;p&gt;Out of the 6 products that I’ve built, 3 died.
The &lt;a href=&quot;/#books&quot;&gt;two books&lt;/a&gt; are still selling, however the sale volume went down significantly.
I attribute it to the tech recession.
&lt;a href=&quot;https://remoteornot.fyi&quot;&gt;Remote or Not&lt;/a&gt; is kinda cool, I’ll admit it, but I don’t really know what to do with it.
And I’m still exploring growth opportunities for &lt;a href=&quot;https://justfaxonline.com/en&quot;&gt;JustFax Online&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-struggle-is-real&quot;&gt;The struggle is real&lt;/h2&gt;
&lt;p&gt;I went a long way.
From a software engineer with &lt;em&gt;“build it, and they will come”&lt;/em&gt; mentality, all the way to &lt;em&gt;“I don’t care about tech that much anymore, how do I make money?”&lt;/em&gt;
I started my journey with &lt;em&gt;“let’s build a cheaper alternative to LaunchDarkly.”&lt;/em&gt;
And so I did.
But nothing happened.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Oh. There is this thing called marketing”&lt;/em&gt;—I thought to myself.
And so I started to delve deeper into SEO, marketing, and audience building (drop a follow by the way: &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;Twitter&lt;/a&gt;/&lt;a href=&quot;https://mstdn.social/@skwee357&quot;&gt;Mastodon&lt;/a&gt;).
I’ll talk more about the pros and cons of indie-hacking, and &lt;em&gt;#buildinpublic&lt;/em&gt; communities in a different post, but I have witnessed a lot.&lt;/p&gt;
&lt;p&gt;I have witnessed how dreams shatter.
How people disappear.
I would follow someone on Twitter, see all the progress they’d make, and then, one day, &lt;strong&gt;poof&lt;/strong&gt;—they are gone.
Some, simply stop posting.
Who knows, maybe they went silent.
Others, just shifted their focus to a new job, and are no longer interested in indie-hacking.
And I started to hate this term, &lt;strong&gt;indie-hacker&lt;/strong&gt;.
But that’s a topic for a different post.&lt;/p&gt;
&lt;p&gt;And so yeah, the struggle is real.
I often see newcomers on various online communities that post something along the lines of “&lt;em&gt;It’s not a serious project, I just want to make &lt;strong&gt;$1-2k/mo&lt;/strong&gt; from it&lt;/em&gt;”.
My friend!
$1-2k/mo is a lot of money.
And it’s damn hard to get there.
Sure, luck plays a major role, although many will disagree with me.&lt;/p&gt;
&lt;p&gt;Should you start?
I don’t know.
I’m not your dad.
You can, but chances are you will quit.
I’ve been doing it on-and-off for &lt;strong&gt;14 years&lt;/strong&gt;, and I’m far from being &lt;a href=&quot;https://paulgraham.com/ramenprofitable.html&quot;&gt;Ramen profitable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’m not gonna lie, but it was way easier during the first 6 months.
Sure, I was burning cash reserves, but I was working all day, (almost) every day.
I wrote 2 books, launched 5 products.
Success comes from trying and failing (and a bunch of luck), so the more you build, the closer you get to success.&lt;/p&gt;
&lt;p&gt;I would definitely not recommend you quitting your job if you don’t have a good amount of savings to sustain your unemployment.
Some people do it, and I think the chances for success are higher, simply because psychologically you don’t have a choice.
With a full-time job, your brain is kind-of &lt;em&gt;“well, maybe it’s Ok if you quit, you still have your job that makes money”&lt;/em&gt;.
Shut up, brain!&lt;/p&gt;
&lt;p&gt;But it’s possible to do it as a full-time employee, and even have a life.
I’ve built a routine where I train 5 times a week, spend time with my wife, work a full-time job, and able to work on multiple side-projects.
Sure, I’ve sacrificed a lot.
My social life is practically non-existent, partly because I’ve migrated to a new country, far away from my family and friends.
I don’t play video games, or watch TV-series.&lt;/p&gt;
&lt;p&gt;Many people online ask how is it possible to juggle all of these things.
Well, it’s not possible if you are not willing to make sacrifices.
But everyone wants everything.
This is why many quit.
They realize that making $1k/mo is not as easy as they make it sound, and they are not willing to sacrifice things, or make changes to their lifestyles.
I don’t blame them, to each their own.
Being an entrepreneur is not the holy grail for everyone.&lt;/p&gt;
&lt;p&gt;But it is for me.
Although deep inside I have a feeling that once I’ll achieve my goals, there will just be something else to strive for.
And then, the once unattainable goal of becoming an entrepreneur, will just become a routine.&lt;/p&gt;
&lt;p&gt;Until then, see you on this blog.&lt;/p&gt;</content:encoded></item><item><title>Tips for improving your CV</title><link>https://kudmitry.com/articles/tips-for-improving-your-cv/</link><guid isPermaLink="true">https://kudmitry.com/articles/tips-for-improving-your-cv/</guid><pubDate>Mon, 17 Jun 2024 06:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I’ve seen a lot of people online asking for CV improvement tips.
Most of them make the same mistakes, and while I tried to help some of them, eventually I realized that I repeat myself too often.
So I decided to share some tips for CV improvement from someone who’s been on both sides of the interviewing process, and is married to a tech recruiter.
So let’s begin.&lt;/p&gt;
&lt;h2 id=&quot;who-reads-your-cv&quot;&gt;Who reads your CV?&lt;/h2&gt;
&lt;p&gt;Before we begin to talk about your CV, we need to understand who reads them, and what’s their purpose.
Excluding any automation and AI tools that might or might not exist nowadays, the first person to view your CV—is a tech recruiter.
Their role is to do a quick match between your skills and the position.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;In smaller companies, the first person to view your CV might be an engineer or a hiring manager.
This does not change the role of the CV in any way.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A recruiter, on average, spends between 6 and 8 &lt;strong&gt;seconds&lt;/strong&gt; per resume.
Why?
Because some positions get hundreds, if not thousands of resumes.
Many of them are irrelevant for the position, location, experiences, skills, etc.
Some CVs are needlessly long, or contain absolutely irrelevant information.
Therefor, we, humans, develop optimization mechanisms.&lt;/p&gt;
&lt;p&gt;A recruiter will try to scan your CV for recognizable patterns.
They will then do an assessment based on these patterns, and if your CV passes the initial assessment, the recruiter will spend time reading the relevant parts.&lt;/p&gt;
&lt;p&gt;It’s important to clarify that this does not tell anything about how good or bad the recruiter is.
You might think that it’s unfair that recruiters resort to quick scanning rather that reading your entire CV, but think about it this way: do you &lt;strong&gt;really&lt;/strong&gt; read every line of 1500 lines modification PR?
Chances are, you skim.
Because if you wouldn’t, we would live in a perfect world where there would be no bugs or security vulnerabilities.&lt;/p&gt;
&lt;p&gt;So look at it as a fact of life.
These are the rules of the game, and you can either play it, or go be angry at how unfair the world is.&lt;/p&gt;
&lt;h3 id=&quot;how-to-style-your-cv-to-catch-the-eye&quot;&gt;How to style your CV to catch the eye&lt;/h3&gt;
&lt;p&gt;As I mentioned earlier, people who read your CV, resort to quick scanning.
This is why we want to catch their attention.
There are many ways to get the attention of the reader, and here are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using bullet lists instead of long walls of text&lt;/li&gt;
&lt;li&gt;Structuring your text logically with headers&lt;/li&gt;
&lt;li&gt;Highlighting &lt;strong&gt;relevant&lt;/strong&gt; achievements and skills by using text formatting such as &lt;strong&gt;bold&lt;/strong&gt; or &lt;em&gt;italic&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Removing unnecessary information that shadows the important parts&lt;/li&gt;
&lt;li&gt;Rephrasing complicated sentences (for example, instead of saying “in a logical way”, you can say “logically”, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-structure-your-cv&quot;&gt;How to structure your CV?&lt;/h2&gt;
&lt;p&gt;Now that we understood who looks at your CV and in what manner, let’s talk about some basic structuring elements of the CV.&lt;/p&gt;
&lt;h3 id=&quot;header&quot;&gt;Header&lt;/h3&gt;
&lt;p&gt;Header is the first thing that catches the eye of the reader.
This is like your business card, and it should tell the person who sees it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who are you?&lt;/li&gt;
&lt;li&gt;What do you do?&lt;/li&gt;
&lt;li&gt;Where your work can be found?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some people go crazy in their header and include things like their picture, full address, or hobbies.
All these things are irrelevant, and some of them can even harm.
I will discuss things you should not put in your CV later in this article.&lt;/p&gt;
&lt;p&gt;Your header should include at minimum: Your full name, relevant title for the position, and contact details like email and/or phone number.
It’s also advisable to add links to places that showcase your work or contributions.
This could include links to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;GitHub/GitLab/BitBucket—a place where you can showcase your projects&lt;/li&gt;
&lt;li&gt;StackOverflow&lt;/li&gt;
&lt;li&gt;Blog or personal website/portfolio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use common sense.
If you don’t have an active GitHub, don’t link it.
Same for StackOverflow, LinkedIn and any other website.
If you blog about food recipes with mushrooms, but applying for a software engineer position, your blog is a &lt;strong&gt;distraction&lt;/strong&gt;, and should not be in your CV.&lt;/p&gt;
&lt;p&gt;Last tip: links should be clickable and visually presented as links (for example: using blue text).
The last thing you want is for the person to mistype your email, and you will never get your interview appointment.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Summary is a very contradicting block.
The general advice is to skip summary all together, and instead use cover letter if the job application requires it.
If you do want to include summary, and have the necessary space (I will discuss later about the length of the CV), then you should—(a) keep it short, and (b) explain why you are a good fit for the position, rather than talking about your life journey.&lt;/p&gt;
&lt;h3 id=&quot;education&quot;&gt;Education&lt;/h3&gt;
&lt;p&gt;If you have a relevant degree (read: computer science or mathematics), include them at the top of your CV (after header and summary).
Irrelevant degrees (sociology, psychology, architecture), should be included in the end of the CV.&lt;/p&gt;
&lt;p&gt;Make sure to highlight relevant honorary titles, and include any high scores, achievements, relevant publications, projects, and researches.&lt;/p&gt;
&lt;p&gt;This section should be short, and to the point.
Ideally a one-liner per degree for non internship or junior positions.&lt;/p&gt;
&lt;p&gt;For people without degrees, remember that there are plenty of talented, self-taught developers who work, and are successful in the industry.
You should still apply to positions that require degrees if you believe that you can compensate the lack of degree and showcase your strong side in the CV.&lt;/p&gt;
&lt;h3 id=&quot;work-experience&quot;&gt;Work experience&lt;/h3&gt;
&lt;p&gt;This is the most important section of your CV, and should include, in reverse chronological order, all your work experience, internships, contracts, projects, etc.&lt;/p&gt;
&lt;p&gt;Each position, including different roles in the same company, should be visually separated, while all roles in one company should be grouped together.
This presents growth in the company in a visual way to the eyes of the reader.
You should also include tenure for each role.&lt;/p&gt;
&lt;p&gt;Each company block can include 1–2 sentences about the company and/or the products.
Afterward, mention your role(s) together with these 3 points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Things done&lt;/li&gt;
&lt;li&gt;Achievements and impact&lt;/li&gt;
&lt;li&gt;Tech stack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Things done&lt;/strong&gt; are, well, things you’ve done while performing the role.
It’s always better to back it up with numbers.
Here are some great examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduced&lt;/strong&gt; response time from 1.5s to 200ms in a &lt;strong&gt;critical&lt;/strong&gt; part of the system&lt;/li&gt;
&lt;li&gt;Increased sales by &lt;strong&gt;30%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Implemented TDD which caused a &lt;strong&gt;reduction of 25%&lt;/strong&gt; in runtime in our CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice how each sentence is (a) phrased in past tense, (b) is part of a bullet list, and (c) highlights important information.
Now, instead of trying to explain you why this is better, let me just write the same information in a paragraph and let you be the judge.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I work at X for Y years, in a team.
During my time there I managed to achieve a lot.
One great example is when I was able to write a piece of code that reduced the response time drastically in a critical part of the system.
This was great, because it helped the company to increase sales.
I was also able to implement a test driven methodology which helped cut the run time of our CI/CD pipeline by 20 seconds.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One word: &lt;strong&gt;BORING&lt;/strong&gt;.
Ain’t nobody got time to read it.
It’s like a dull novel.
It says nothing by using so many words and mental capacity.
There are no numbers and no reference points, but instead ambiguous words like “drastically” and “increase”.
The only number here is “20 seconds”, but even then it’s useless, because if your pipeline was running for 17 minutes, 20 second reduction is not an achievement I’d put in my CV.
25%, on the other hand, is.&lt;/p&gt;
&lt;p&gt;There is another psychological trick you can use called “framing”.
For example, a reduction in response time from 600ms to 400ms, might not sound impressive.
On the other hand, 33% reduction—is impressive, even though it’s the same information.&lt;/p&gt;
&lt;p&gt;Before we move to the next section, one word about tech stack.
There are two approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding tech stack per position&lt;/li&gt;
&lt;li&gt;Adding a separate CV section for tech stack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recommend using the first approach, as this indicates two things: (1) showcasing your most recent tech stack, and (2) might show a progression and/or growth.&lt;/p&gt;
&lt;h3 id=&quot;additional-information&quot;&gt;Additional information&lt;/h3&gt;
&lt;p&gt;Some more information that you can include in the CV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spoken languages&lt;/li&gt;
&lt;li&gt;Openness to relocation, if applicable&lt;/li&gt;
&lt;li&gt;Links to relevant publications, technical papers, patents, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;common-mistakes-in-making-your-cv&quot;&gt;Common mistakes in making your CV&lt;/h2&gt;
&lt;h3 id=&quot;biased-information&quot;&gt;Biased information&lt;/h3&gt;
&lt;p&gt;By far, one of the biggest mistakes people make in their CV, is to include biased information.
They might think it’s cute that they can bake cupcakes, or like to put their photo in the CV, while in reality it might play against them.&lt;/p&gt;
&lt;p&gt;I’m not going to talk in depth about biases, why they exist, and how the world is unfair.
Instead, I’ll leave you with that: this is a game, and you need to play by certain rules in order to be able to compete and win this game.&lt;/p&gt;
&lt;p&gt;We, humans, all have biases.
And even though we undergo corporate trainings, you can’t go against your nature.
Your interviewer might dislike dogs, or be allergic to cats.
The fact that you dedicate a sentence, or God forbid a paragraph, of the CV to your beloved furry friend—might bias the interviewer to look for reasons to reject you.&lt;/p&gt;
&lt;p&gt;Here is a non-exhaustive list of things to not include in your CV: photo, hobbies, marital status, location, sex, race, culture, religion, age (including date of birth), disabilities, etc.
Having said that, there might be laws in your country, or the country where you apply in, which require you to disclose certain information, so get yourself educated on that matter.&lt;/p&gt;
&lt;h3 id=&quot;unnecessary-information--generalization--too-long-of-a-cv&quot;&gt;Unnecessary information / Generalization / Too long of a CV&lt;/h3&gt;
&lt;p&gt;The second most common mistake—is a very long CV and/or CV that tells the reader nothing about you and your experience.
Firstly, let’s talk about CV length.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your CV must be 1 page long.&lt;/strong&gt;
If you have more than 8 years of experience, it’s fine if your CV becomes 1.5 to 2 pages.
Here are some tips how you can reduce the length of your CV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Removing irrelevant information&lt;/li&gt;
&lt;li&gt;Replacing terminology with abbreviations: &lt;em&gt;Database&lt;/em&gt; becomes &lt;em&gt;DB&lt;/em&gt;, &lt;em&gt;Pull Request&lt;/em&gt; becomes &lt;em&gt;PR&lt;/em&gt;, etc&lt;/li&gt;
&lt;li&gt;Avoiding “walls of text”&lt;/li&gt;
&lt;li&gt;Playing with font size and formatting options&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In case you still struggle with CV length, consider limiting information about your oldest experience.
Remember, most interviewers are interested in your recent experience anyway.&lt;/p&gt;
&lt;p&gt;Lastly, I want to touch the topic of unnecessary information and generalization.
These two things contribute to CV length as well, and avoiding them can help you make your CV leaner while providing more context to the reader.&lt;/p&gt;
&lt;p&gt;Avoid adding any information that is not relevant to the job/position/industry.
Your future team lead does not care about the fact that you were the beer drinking champion in high school, despite the fact that you consider it an honorary badge.
Instead, they care about your experience in impact in past jobs.
This is why it’s important to make your CV concise and to the point.&lt;/p&gt;
&lt;p&gt;Instead of writing vague statements like “&lt;em&gt;Designed an internal system&lt;/em&gt;”, try to answer questions such as “&lt;em&gt;What system?&lt;/em&gt;”, “&lt;em&gt;Why it was needed?&lt;/em&gt;”, “&lt;em&gt;What was the impact of this system on the product/company/team?&lt;/em&gt;”.
Every sentence in your CV should show impact and explain—better with numbers—the value of the idea, task performed, or experience gained.
Remember, you have to explain years of your work in a few sentences, why not focus on the most important things that really demonstrate your growth and the value your provide(d) for the company?&lt;/p&gt;
&lt;h3 id=&quot;design--format--spelling&quot;&gt;Design / Format / Spelling&lt;/h3&gt;
&lt;p&gt;The last mistake people make with their CVs is related to the design and the format of the CV.&lt;/p&gt;
&lt;p&gt;The only acceptable format of the CV is &lt;strong&gt;PDF&lt;/strong&gt;.
Other formats depend a lot on the software in which they are viewed, so a &lt;code&gt;.docx&lt;/code&gt; file created on your computer, might be completely messed up on the computer/software of your reader.
PDF does not have these issues.&lt;/p&gt;
&lt;p&gt;In terms of design, your CV should be monotonous color and text only.
Avoid any graphics such as images, or complex formatting options such as tables or forms.
Use neutral colors: white background, black text, gray headers, blue hyperlinks.
Avoid using anything that can distract the reader from the main purpose of your CV.&lt;/p&gt;
&lt;p&gt;Lastly, make sure that your CV is professional.
This means two things.
&lt;strong&gt;First&lt;/strong&gt; is spelling and grammar—make sure to reread it before submitting, give someone to proofread it, or use spellchecking software.
&lt;strong&gt;Second&lt;/strong&gt;, make sure your CV is written in the expected language.
Some companies want a CV in English, while others might request CV in country’s locale language.&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;
&lt;p&gt;Your CV is a gateway to your experience.
On one hand you want to put as much information as possible in there; on the other hand, you need to understand that CVs are rarely read thoroughly, at least not in the screening phase.
This is why it’s important to craft your CV in a way that you can both (a) present your experience, and (b) catch the eye of the reader.
Your CV could be deciding factor of whether you will be contacted back by the company and invited for interview.&lt;/p&gt;
&lt;p&gt;However, there is more to CV that what I wrote in this article.
I didn’t touch things like LinkedIn, which is preferred nowadays more than CVs; covers letters; blogs and other social networks.
Moreover, CV is just one step in the interviewing process.&lt;/p&gt;
&lt;div class=&quot;&quot;&gt;
&lt;img src=&quot;/images/from-applicant-to-employee_cover.png&quot; class=&quot;float-left mr-4 my-1 py-1&quot; width=&quot;140&quot; alt=&quot;From applicant to Employee book cover&quot;&gt;
&lt;p&gt;
This is the reason I teamed up with my wife, a Senior Talent Acquisition Specialist in tech, and we wrote the book &lt;a href=&quot;/book/from-applicant-to-employee/&quot;&gt;From Applicant to Employee - Your blueprint for landing a job in tech&lt;/a&gt;.
This book covers the entire interviewing process, and this article is based on chapter 2 from the book: Building your CV.
If you believe this book can help you with your job search, consider buying it on &lt;a href=&quot;https://kudmitry.gumroad.com/l/from-applicant-to-employee&quot; target=&quot;_blank&quot;&gt;Gumroad&lt;/a&gt; or &lt;a href=&quot;https://leanpub.com/from-applicant-to-employee/&quot; target=&quot;_blank&quot;&gt;LeanPub&lt;/a&gt; in order to support us and this blog.
&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title>Why your team might be holding you back</title><link>https://kudmitry.com/articles/your-team-might-be-holding-you-back/</link><guid isPermaLink="true">https://kudmitry.com/articles/your-team-might-be-holding-you-back/</guid><pubDate>Fri, 10 May 2024 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About a week ago, my wife and I went to our first escape room in the Netherlands.
We’ve been in about 12-15 escape rooms before, and were always able to escape as a team of two.
Not this time, however.
This escape room was the first where we failed to escape.&lt;/p&gt;
&lt;p&gt;Poisoned by the corporate world I decided to do a postmortem of what went wrong, and what we could have done better in order to increase our chances to escape.
We both came to a conclusion that adding two more people would have increased our chances to escape, as the room relied a lot on multitasking in its first phase.
When we tried to think who we would like to take with us, if we could, we came to a conclusion that most of our friends would &lt;strong&gt;not&lt;/strong&gt; be helpful.&lt;/p&gt;
&lt;p&gt;Some, we don’t know well enough.
Others are as competitive as us, and a team of 4 leaders can lead to only one outcome: failure due to inability to follow rather than lead.
This recent experience, together with some past thought about software engineering as a team sport, made me reconsider the accepted norm of software engineering teams.&lt;/p&gt;
&lt;h2 id=&quot;why-your-team-might-be-holding-you-back&quot;&gt;Why your team might be holding you back&lt;/h2&gt;
&lt;p&gt;It’s pretty common to accept the fact that software is developed in teams.
Solo developers are an exception, and any successful MVPs that are built by 2 people, are going to grow into a big development team eventually.
However, developing software in teams is not the be-all end-all solution.
In fact, there are some issues that arise strictly from the fact that we have a team.
Let’s look at these issues.&lt;/p&gt;
&lt;p&gt;There are two ways to define a team on a given project.
A team can be a combination of multiple disciplines like backend developer, frontend developer, QA engineer, designer and DevOps.
But a team can also be 7 backend engineers.
The former are less prone to team issues, however some issues also applies to multidiscipline teams.&lt;/p&gt;
&lt;p&gt;The first issue I would like to discuss is the obvious concept on stepping of each other toes.
When a team is too big, people start to work on overlapping functionalities.
We try to pretend that we can solve it with feature branches and occasional rebase—in reality we can’t.
The bigger the team, the more they step on each other toes.&lt;/p&gt;
&lt;p&gt;Another issue with team work is the fact that not all team members are on the same page.
You see, in reality, if we look at software companies, we assume they have one goal—build product for their customers and make money.
As an engineer who joined such company, ideally you should inherit this goal.
In reality, however, this is not true.
Due to corporate BS and things like &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-curse-of-the-senior-engineer/&quot;&gt;career ladders&lt;/a&gt;, every team member has a hidden agenda.
It’s like these board games when you all work as a team towards common goal, but the actual winner of the game is the one who fulfills his/her hidden agenda.
One such game is “The Resistance”, where everybody claims to be member of the resistance who tries to overthrow the government, but at least two people (depending on the size of the group) are secret spies who try to sabotage the resistance.&lt;/p&gt;
&lt;p&gt;I’m not saying that in a corporate world, members of a team try to sabotage the entire team, but I am claiming that people have hidden agendas.
It’s even part the growth plan.
So members of the team might get tasks like “lead the design of a complex feature” in order to score points in their leadership qualities so that they can get promoted in the next promotion cycle.
This creates a weird dynamic inside the team because team members would focus on reaching their personal goals while putting the company goal on a second place.&lt;/p&gt;
&lt;p&gt;Composition of the team also affects its success.
If we look at job requirements, we can agree that everyone wants the best of the best.
They want the top performers, and A-players, rock stars, and ninjas.
And while in theory it sounds great, in reality it’s a recipe for disaster.&lt;/p&gt;
&lt;p&gt;If we look back at my escape room example, my wife and I wouldn’t take any of our friends with us.
Maybe to have fun together, but not if our goal is to escape.
We both have successful friends who are motivated and posses leadership qualities.
Smart and successful people, usually, don’t like authority.
In order to be a leader you need to be able to question the norm, and you don’t like to follow orders.&lt;/p&gt;
&lt;p&gt;The same can be said for top performing software engineers.
They are top performers, or A-players, because they question authority and dare to change what’s accepted.
Gather a team of such people together, try to appoint one to be the lead, and you will get into constant refactoring sessions as nobody will be satisfied with the code quality of his peers.
But companies seem to ignore it.&lt;/p&gt;
&lt;p&gt;Lastly, team size correlates with growth.
The other day I was watching an interview with Pavel Durov, the creator of VK and Telegram messenger.
Telegram is a messenger that is on a path to reach 1 billion monthly active users, while having only about 30 engineers according to Pavel.
When asked by interviewer why other companies have so much engineers, Pavel shared an interesting insight.
It’s his words, so take it as you wish.
He said that he met with Jake Dorsey at a time and told him that Twitter does not need that many engineers in order to run.
Dorsey agreed with Pavel, and in reply said that he can’t downsize the teams as it will signal that something is wrong with the company.&lt;/p&gt;
&lt;p&gt;And while I can’t attest to the existence of such conversation between the two, I can agree with a clear fact that team size equals growth.
The moment the project is deemed as important, higher management will allocate monetary resources primary for one use-case: hiring.
It becomes the number one priority of the team lead—to grow his team.
And it creates a double-edged sword.
From inside the company, team growth correlates with success of the product.
From outside, candidates don’t want to work in small teams as they might see the project not as important as project that has a bigger team.&lt;/p&gt;
&lt;h2 id=&quot;the-lone-wolf&quot;&gt;The lone-wolf&lt;/h2&gt;
&lt;p&gt;People who like or want to work alone, are not viewed well in the eyes of the corporate world.
Every job requirement wants people who &lt;em&gt;can&lt;/em&gt; work alone if needed, usually because companies don’t want to babysit you or save cost to avoid investing in proper documentation and/or trainings;
but you also need to be able to work in a team and be a great team player, which usually means that you need to play the corporate rules of pointless meetings and other rituals around development cycles.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sive.rs/about&quot;&gt;Derek Sivers&lt;/a&gt; wrote a nice paragraph about it:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Nobody gives a novelist shit for writing alone. But an entrepreneur, programmer, or musician is expected to collaborate. I disagree, for me. I prefer the life of a novelist, whether I’m writing code, music, or systems.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And I’d like to agree with Derek here.
The moment I switched to a small team (we are two people), I must say that I find it way more effective compared to 5+ people teams I had before.
There are no hidden agendas as we don’t participate in the ladders games.
Nor we step on each other toes, as the product scope is huge.
While infrastructure related code affects both of us, individual feature development is diverse enough that we can operate each by himself.&lt;/p&gt;
&lt;p&gt;I believe that modern software development practices were inherited from wrong disciplines.
Software development viewed more as a factory assembly line where more people equals higher output, but it’s far from the truth.
A lot of the modern team dynamics are flawed, and they tend to harm productivity of each individual, thus sabotaging the entire team.
I think that software engineering would benefit more if we would focus on one-man-teams or small, less than 3–4 people, teams.
This configuration provides higher development velocity, and reduces knowledge fragmentation.&lt;/p&gt;
&lt;p&gt;Development velocity is improved because nobody has hidden agendas, people don’t step on each other toes, and there is less overhead in managing the team, thus you are able to get rid of rituals such as stand-ups, plannings, retrospectives, etc.
Knowledge fragmentation is reduces because each individual has bigger knowledge of the system.
With bigger teams, in order to avoid stepping on each other toes, each engineer is given a smaller subset of the system, thus known as “a cog in the machine”.&lt;/p&gt;
&lt;p&gt;Consider the above the next time you want to join a big team or scale your own team.&lt;/p&gt;</content:encoded></item><item><title>xz backdoor</title><link>https://kudmitry.com/articles/xz-backdoor/</link><guid isPermaLink="true">https://kudmitry.com/articles/xz-backdoor/</guid><pubDate>Thu, 04 Apr 2024 08:45:00 GMT</pubDate><content:encoded>&lt;p&gt;A malicious backdoor was discovered in &lt;code&gt;xz&lt;/code&gt; library that implements LZMA compression.
&lt;code&gt;xz&lt;/code&gt;, among many other places, is used, indirectly, in &lt;code&gt;sshd&lt;/code&gt;.
My attempt to explain what happened.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;warning&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;WARNING&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I’m not a security researcher.
I did not analyze the backdoor, and this post is my attempt to make sense of what happened in simple words.&lt;/p&gt;&lt;p&gt;I’ll link to as much content as possible from people who are more competent than me.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;timeline&quot;&gt;Timeline&lt;/h2&gt;
&lt;p&gt;Around the year of 2005, Lasse Collin together with other people, creates the &lt;code&gt;xz&lt;/code&gt; library that implements the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm&quot;&gt;LZMA&lt;/a&gt; compression algorithm.
LZMA is a lossless compression algorithm.
Over the time, &lt;code&gt;xz&lt;/code&gt; becomes widely adopted in compressing data, and you’ve probably had to download at least one file that has the extension of &lt;code&gt;.tar.xz&lt;/code&gt;.
Most of the work on &lt;code&gt;xz&lt;/code&gt; was done by Lasse Collin, who, as we learn now, had personal struggle and not a lot of time to maintain the project.
This setup was perfect for some social engineering.&lt;/p&gt;
&lt;p&gt;At the end of 2021, we meet another person named Jia Tan.
Jia Tan starts to create little patches to &lt;code&gt;xz&lt;/code&gt;, but they are not merged.&lt;/p&gt;
&lt;p&gt;Forward to mid 2022, another person, named Jigar Kumar, complains that the development on &lt;code&gt;xz&lt;/code&gt; has stalled, and that the original maintainer should give the project to someone else.
An exchange of accusations from Jigar Kumar, and explanations from Lasse Collin, take place in the following months from April till early July 2022.
Eventually, Jia Tan becomes a maintainer of &lt;code&gt;xz&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s important to note few things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, Lasse Collin is a one person that maintains a free open source library.
A lot has been said on the state of open source and how maintainers treated unfairly.
Companies build a billion dollar products on top of free and open source software, while rarely giving anything back.
While the accusations from Jigar Kumar were probably part of social engineering, it’s not uncommon that maintainers of OSS are being harassed and demanded to patch their projects or keep developing them.
There are no indications that Lasse Collin was part of the execution, and everything points out to the fact that he is just a guy who maintained a fairly popular open source library, and had personal health issues and lack of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Secondly&lt;/strong&gt;, there are no proofs that both Jia Tan and Jigar Kumar are real individuals (rather than state actors); not the same person; didn’t know each other; none of this was planned.
Jigar Kumar does not appear anywhere else on the internet apart from a few mailing list exchanges where he pushes Lasse Collin to give up the project to another person.
Jia Tan does not appear anywhere else on the internet apart from small commits to &lt;code&gt;xz&lt;/code&gt; about 2 years ago in order to build credibility and gain trust.&lt;/p&gt;
&lt;p&gt;I’ll skip some other technical milestones in the timeline.
You can read a detailed explanation by &lt;a href=&quot;https://research.swtch.com/xz-timeline&quot;&gt;Russ Cox&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-backdoor&quot;&gt;The Backdoor&lt;/h2&gt;
&lt;p&gt;The TL;DR; version is that Jia Tan goes on to create a very sophisticated backdoor that hides inside compressed &lt;code&gt;xz&lt;/code&gt; (irony?) files that are, presumably, used for testing.
This is smart, because nobody would suspect a compressed file inside &lt;code&gt;tests/&lt;/code&gt; folder to be malicious.
In fact, it’s common practice to have mock data for tests, and these things are rarely checked during PRs, as we assume they are just test data.&lt;/p&gt;
&lt;p&gt;The backdoor, which is hidden in multiple compressed files, taps into the build process of the &lt;code&gt;xz&lt;/code&gt; library—essentially replaces the &lt;code&gt;RSA_public_decrypt&lt;/code&gt; function symbol with a malicious function that gives an attacker, who possess a particular private key, &lt;strong&gt;remote execution capabilities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This means that when SSH server is launching, it tries to load a dynamic library and looks for &lt;code&gt;RSA_public_descrypt&lt;/code&gt; function, which is a legit function.
However, if the distributed &lt;code&gt;xz&lt;/code&gt; library is malicious, then it modifies the way the dynamic library resolves &lt;code&gt;RSA_public_descrypt&lt;/code&gt; function, and instead it loads a malicious code.
This code listens to a particular payload that is encrypted by a private key, that apparently only Jia Tan have, and using the embedded public key inside the code, it validates the payload (by checking that it was signed by Jia Tan’s private key)—and if so, it executes whatever is in the payload.&lt;/p&gt;
&lt;p&gt;SSH runs as root.
It has to, because it needs to de-escalate your privileges down to the particular user account that you log in with.
This means that using remote code execution backdoor, one could bypass the entire account management and gain root access to a Linux machine that runs SSH.
In case if you are not aware, this means millions, if not more, of machines all around the world.
From web servers, to networking equipment, industrial equipment management systems (manufacturing, power plants, military and civilian objects), personal and home appliances, etc.
Everything that runs SSH using &lt;code&gt;systemd&lt;/code&gt;, on a rolling release Linux distribution, is vulnerable.&lt;/p&gt;
&lt;p&gt;The backdoor was assigned the &lt;code&gt;CVE-2024-3094&lt;/code&gt; identifier, and has the base score of &lt;strong&gt;10.0 CRITICAL&lt;/strong&gt;, the highest possible severity.&lt;/p&gt;
&lt;h2 id=&quot;discovery&quot;&gt;Discovery&lt;/h2&gt;
&lt;p&gt;The backdoor was, accidentally, discovered by Microsoft employee and PostgerSQL contributor Andres Freund, when he was benchmarking PostgreSQL and noticed that his SSH connection took a hundred milliseconds longer and used more CPU cycles.
He first thought that it has to do with a compromised Debian package, but later found out that the entire upstream xz repository has been compromised.
If he hadn’t discovered it, there is a good chance that this vulnerability could have been reached major Linux distributions, and even production servers and systems.&lt;/p&gt;
&lt;p&gt;There are discoveries now that Jia Tan was in talks with major Linux distributions to push his malicious version of &lt;code&gt;xz&lt;/code&gt;.
One such example, is &lt;a href=&quot;https://bugs.launchpad.net/ubuntu/+source/xz-utils/+bug/2059417&quot;&gt;this bug request for Ubuntu&lt;/a&gt; to update to &lt;code&gt;xz&lt;/code&gt; to 5.6.1.&lt;/p&gt;
&lt;p&gt;Andres shared his finding in an &lt;a href=&quot;https://www.openwall.com/lists/oss-security/2024/03/29/4&quot;&gt;Openwall mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;who-is-behind-the-backdoor&quot;&gt;Who is behind the backdoor?&lt;/h2&gt;
&lt;p&gt;As of the time of publishing this article, there are no evidence that Jia Tan/Jigar Kumar are real individuals, and the consensus in the security community, is that a state actor is behind this attack.
It seems like this backdoor was at least 2 years in the making, carefully planned and executed.
Neither Jia Tan nor Jigar Kumar exist outside the few posts on mailing list of &lt;code&gt;xz&lt;/code&gt;.
Jia Tan’s role was to write the backdoor, while Jigar Kumar’s role was to pressure Lasse Collin to pass &lt;code&gt;xz&lt;/code&gt; to another maintainer.&lt;/p&gt;
&lt;p&gt;While this attack could have been carried out by an individual, or two, the fact that it was almost three years in the making, and the level of sophistication, points out to either an organized group, or a state actor.
I could name at least 6 state actors who have the ability and the desire to plan and execute such attack, given the geopolitical situation in the past 3 years.
I will, however, refrain from doing so, as the situation is still unfolding.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;I want to come back to &lt;a href=&quot;https://xkcd.com/2347/&quot;&gt;XKCD #2347&lt;/a&gt;.
It clearly illustrates the modern digital infrastructure and the role that OSS plays in it.&lt;/p&gt;
&lt;p&gt;We tend to forget that, but most of the modern digital infrastructure is run on the backs of hobby projects that few people maintain in their free time.
These people are prone to losing interest, burn out, and other personal problems.
And they are, like everyone else, prone to social engineering.&lt;/p&gt;
&lt;p&gt;It’s unfortunate that Lasse Collin found himself in that situation.
At one point, his GitHub account was even suspended (but it seems like it was reinstated few days ago).
I don’t think he had any malicious intents, and he was just a guy maintaining a fairly popular open source library.&lt;/p&gt;
&lt;p&gt;But we also need to remember that while the world became a safer place, there are still malicious actors in it.
It is impossible to audit every single line of code out there.&lt;/p&gt;
&lt;p&gt;Time will tell how we will move forward with OSS and our digital infrastructure, but as of today, we are thanking Andres who cares about CPU cycles, even in a world where everyone seems to solve their performance problems with “just buy more RAM”, and “let’s spin another EC2 instance”.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cve.org/CVERecord?id=CVE-2024-3094&quot;&gt;CVE-2024-3094&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/XZ_Utils_backdoor&quot;&gt;XZ Utils Backdoor Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.schneier.com/blog/archives/2024/04/xz-utils-backdoor.html&quot;&gt;XZ Utils Backdoor - Schneier on Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openwall.com/lists/oss-security/2024/03/29/4&quot;&gt;Andreas Freund - backdoor in upstream xz/liblzma leading to ssh server compromise - Openwall&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tukaani.org/xz-backdoor/&quot;&gt;XZ Utils backdoor - Lasse Collin | Tukaani&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/thesamesam/223949d5a074ebc3dce9ee78baad9e27&quot;&gt;FAQ on the xz-utils backdoor (CVE-2024-3094) - Github Gists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://research.swtch.com/xz-timeline&quot;&gt;Timeline of the xz open source attack - Russ Cox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.redhat.com/en/blog/urgent-security-alert-fedora-41-and-rawhide-users&quot;&gt;Urgent security alert for Fedora Linux 40 and Fedora Rawhide users - RedHat Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>The Curse of the Senior Software Engineer</title><link>https://kudmitry.com/articles/the-curse-of-the-senior-engineer/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-curse-of-the-senior-engineer/</guid><pubDate>Thu, 14 Mar 2024 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I started my software engineering career more than 14 years ago.
Back then I was doing some SEO script automation using PHP.
Slowly, I integrated better into the company and started to work on more core features of the product, and eventually even leading my own product.&lt;/p&gt;
&lt;p&gt;I worked 6 years in that company.
While working there, the company grew by approximately 3 people.
This is far from your average hyper growth startup.
We had no titles, no promotions, no performance reviews.
I was happy because I was doing software engineering and calling myself a software engineer.
It’s only when I needed to revamp my CV for a new job search, I had to look back at these 6 years and try to put the “correct” titles to each period of time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web developer at the beginning&lt;/li&gt;
&lt;li&gt;Senior software engineer afterward&lt;/li&gt;
&lt;li&gt;Tech lead when I was leading my own product and a tiny team&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And only when I joined Autodesk, an American corporate, I was exposed to titles, career ladder and promotions.
And I learned that many companies, both small and big, have titles and career ladders.&lt;/p&gt;
&lt;h2 id=&quot;whats-up-with-the-titles&quot;&gt;What’s up with the titles?&lt;/h2&gt;
&lt;p&gt;There are multiple reasons why companies are obsessed with titles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firstly&lt;/strong&gt;, titles help normalize pay range.
You don’t want an engineer with 2 years of experience to be paid the same as one with 6 years of experience.
So you create titles.
The first one will be Junior, and the second one will be Senior.
The pay range for junior is X and for senior is Y.
Often times, companies will have multiple internal titles behind one external title.
For example a Senior Software Engineer as an external title, could be translated to either Sr.SW1 or Sr.SW2 based on different factors.
This allows for further fragmentation of the senior scale.
Instead of putting everyone who has between 5 and 10 years of experience into one big bucket named “Senior”, you would split it between Sr.SW1 who has 5 to, say, 7 years of experience; and Sr.SW2 who has between 7 and 10.
On the outside, both of them would call themselves Senior Software Engineer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt; reason companies have titles is to allow for progression and evaluation.
Titles are usually attached to a set of roles and responsibilities.
A junior engineer might be someone who requires assistance and closer supervision.
The assistance and supervision is usually provided by a senior engineer, thus becoming senior requires that you mentor someone less experienced than you.
Other functions might include things like reach and scope.
A junior engineers work output will impact only his assigned task.
Senior engineer, on the other hand, might improve something in system as he/she works on the assigned task.
A staff engineer is someone who improves something that affects not only his team, but other teams as well.
Examples could be things like introducing a new monitoring tool that is adopted by multiple teams; improving some workflow; etc.&lt;/p&gt;
&lt;p&gt;When the time comes to conduct performance review, your manager will look at your impact inside the team and organization, and based on that will decide whether to promote you to the next level.
With promotion also comes the raise in pay, since levels are tied to pay ranges.&lt;/p&gt;
&lt;p&gt;And when you are interviewing for a company, even though they interview you to an external title like “Senior Software Engineer”, internally they will assign you the company level title based on things like: your compensation, years of experience, how good you passed the interview, etc.
There is a website called &lt;a href=&quot;https://www.levels.fyi/&quot;&gt;levels.fyi&lt;/a&gt; that allows you to look into different career ladders in different companies, and how they compare.&lt;/p&gt;
&lt;h2 id=&quot;the-promotion-dilemma&quot;&gt;The promotion dilemma&lt;/h2&gt;
&lt;p&gt;Often times, failure to get promoted will lead to either you being put on a performance improvement process, or getting fired.
If you look at a career of engineers who stayed for long time in one company, you will see progress.
It could be junior to senior to tech lead.
Or it could be senior to staff to engineering manager.
It all depends on the route in which the engineer want to grow.&lt;/p&gt;
&lt;p&gt;What you won’t see is someone who is stuck in one position for a very long time, usually more than 2–3 years.
Instead, these people would move to a different company which sometimes come with a title improvement.
And because title is tied to salary, this also means salary improvement.
This is why they say that it’s better to go to a different company if you want a pay raise and/or title improvement.
Some companies are either slow, or fail to promote you, while other companies would be happy to do so, based on your years of experience and how well you pass the interview.&lt;/p&gt;
&lt;p&gt;But not everyone is a job hopper, and some people do get stuck in the same company and same title due to laziness, comfort, or other reasons.
These people, unfortunately, are prone to a curse that I call “The Curse of the Senior Software Engineer”.&lt;/p&gt;
&lt;h2 id=&quot;the-curse-of-the-senior-software-engineer&quot;&gt;The Curse of the Senior Software Engineer&lt;/h2&gt;
&lt;p&gt;Before I continue to talk about the curse, and how to avoid it, I want to clarify why it applies mainly to &lt;strong&gt;senior&lt;/strong&gt; engineers.
And seniority, in this case, is referring to reach and scope based on company’s career ladder rather than years of service.
If we look at an engineer’s career, we can split it in two (at least from my experience): Individual Contribution (known as IC) and Leadership.
Everything up to, and including Senior level, falls under IC definition.&lt;/p&gt;
&lt;p&gt;ICs are people who are doing their own work.
They close tasks, fix bugs, carry team duties like on-call schedules.
They might improve the product or the code base, bring their ideas, mentor less experienced engineers, voice concerns and propose resolutions.
But their scope is, mainly, limited to the &lt;strong&gt;individual/team&lt;/strong&gt; level.
They do not have any cross team or organization level impact.&lt;/p&gt;
&lt;p&gt;The moment you cross the boundaries of your own team, you are moving to a Leadership position.
In a healthy organization, Leadership positions are broken into two: Technological Leadership and People Leadership.
Technological leadership includes titles like Staff Engineer, Tech Lead, Principal Engineer, Architect, etc.
People leadership includes titles like Team Lead, Engineering Manager, Group Lead, etc.
You need to reach Senior title in order to be promoted to leadership position.
If you see someone who was promoted to a leadership position from a junior title, this could indicate either unhealthy organization, or incorrect initial title assignment.
Whether you will be promoted to technical leadership or people leadership, is determined by your desire and the career path you have built with your manager.&lt;/p&gt;
&lt;p&gt;But what happens if you reach a Senior level and get stuck there?
There are many reasons to get stuck in a certain level, but they all boil down to either of the two: you fail, or the company fucks up.
You, failing, could be lack of ambition to fight for promotion; lack of desire to be promoted; being comfortable where you are; etc.
A company fuck up might include things like lack of proper promotion protocols; frequent organizational changes which interrupt promotion cycles; in-ability to properly identify who to promote; etc.&lt;/p&gt;
&lt;p&gt;Oh, and there is always a personal factor.
Companies don’t like to discuss it, but we are all humans.
It could be that your manager simply does not like you, and so he/she will try to do their best not to promote you.
This could be a pure humane dislike (they don’t like the way you look, the way you talk, the way you dress, etc.); or it could be due to their own insecurities such as being afraid to become redundant or replaced by you, who is super successful and eager to conquer the world.
And finally, some people are just bad managers.
Companies should have protocols to avoid this from happening, but hey, we are all humans, and sometimes we fuck up.&lt;/p&gt;
&lt;p&gt;So you stay Senior for too long, and you get cursed by the Curse of the Senior Software Engineer.
The curse goes like this:&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;The Curse of The Senior Software Engineer&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;A cursed Senior Software Engineer, is an engineer who has been Senior for too long that upon his or her next job search, they find themselves in between two worlds:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;They are too Senior to be hired as Senior (due to years of experience or salary expectations)&lt;/li&gt;
&lt;li&gt;They have no Leadership experience, or very little of it, to be hired on a Leadership position (tech lead/staff/principal for tech; team lead/engineering manager for people)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;And so they are cursed to roam the Senior valley, and have no ability to enter the tech market.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Stay senior for too long, and there is a high chance that you will get this curse.
How do I know?
Because I judge based on my experience.
I’m a Senior engineer with 14+ years of experience, and was never interested in titles, nor practiced any job hopping for the sake of title improvement.
I like what I do, and I think I’m good at what I do.
But I never felt like playing the corporate game and climbing the career ladder.
I spent the first half of my career in a-typical companies, without any corporate BS, and I can attest that I was successful in what I did.&lt;/p&gt;
&lt;p&gt;However, as I found myself &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;burnt out&lt;/a&gt;, I left the corporate world in pursuit of &lt;a class=&quot;internal new&quot; href=&quot;/articles/6-lessons-6-months-6-projects/&quot;&gt;building a sustainable online business&lt;/a&gt;, with the idea to never return to a traditional workplace.
I have failed however, and in the middle of my 6-month sabbatical, I started to look for a job.
After applying to 100+ positions, I got a bunch of generic rejections.
From those who did not reject me, some told me that I’m overqualified for Senior roles, but under qualified for Leadership positions.
I had to make assumptions as to why I’m getting rejections.
I could have blamed the 2023 tech-recession, but I like to blame myself, so I had to do some self-analysis.&lt;/p&gt;
&lt;p&gt;And while it could be the 2023 tech-recession, if there is a slight chance that my theory is correct, I would like to spread it and inform other engineers to not make the same mistakes as I did.&lt;/p&gt;
&lt;h2 id=&quot;how-to-avoid-the-curse&quot;&gt;How to avoid the curse?&lt;/h2&gt;
&lt;p&gt;There are two ways: you either play by the rules or you exit the game.&lt;/p&gt;
&lt;p&gt;You have to play by the corporate rules and become obsessed with the corporate career ladder.
You need to follow your promotion cycles and make sure you are not stuck at one level for too long.
If you are, you need to stay proactive and consider quitting and moving to a different company.
This move should include a title improvement.&lt;/p&gt;
&lt;p&gt;It sounds cruel, I know.
But this is your career.
Companies, in general, won’t care too much about you.
By the time you will naturally grow for promotion to next level, your manager might change, or the entire company might get restructured.
And while other people, who were more pushy and demanding than you, will get promoted—you will stay behind, slowly acquiring the Curse of the Senior Software Engineer.&lt;/p&gt;
&lt;p&gt;The other option is to exit the game completely.
Remember, FAANG or MANGA, whatever the acronym right now is—dictate the way the industry operates.
If they have titles and career ladder, other hype driven, VC funded startups will follow.
The only way to exit the game, is to find a company that is &lt;strong&gt;not&lt;/strong&gt; like them.
A company that does not care about titles or have performance review rituals.
It’s harder, but companies like this do exist.
This is what I did.
I just found a local startup company (well, in fact, they found me) that does not care about titles and other corporate rituals.
I don’t have daily standups or monthly retrospectives.
We just pull tickets from the backlog and do them.&lt;/p&gt;
&lt;p&gt;Other companies you might consider are big-n-old companies like banks, railway operators, hospitals, etc.
This also includes IT departments in boring standard companies.
Remember, almost every company needs programmers nowadays, but most of them won’t take you to parties in Hawaii, have a hipster office in the coolest building in town with a bearded barista, and attach you the “Full Stack Ninja Squad Leader Engineer” BS title.
This also means they won’t pay you the same money as FAMANGA and Co, and you won’t work with the latest and greatest tech (although exceptions exist).&lt;/p&gt;
&lt;p&gt;Another option you might consider, is switching to self-employment either through freelancing or contracting.
Nobody cares what your title is, or how many years of experience you have, as long as you can deliver the work in time at a reasonable price.&lt;/p&gt;
&lt;p&gt;There is no better or worse decision.
While I don’t see myself coming back to traditional corporate environment (unless forced to), I can understand why people would want to work in Meta or Google and Co.
And if you do, remember that there are certain rules you need to play by.
It’s not enough to “just write code”.&lt;/p&gt;</content:encoded></item><item><title>Serving Astro with Rust</title><link>https://kudmitry.com/articles/serving-astro-with-rust/</link><guid isPermaLink="true">https://kudmitry.com/articles/serving-astro-with-rust/</guid><pubDate>Sun, 10 Mar 2024 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As some of you might know, for the past few months, my free time is spent on &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
I also published few blog posts about this project: &lt;a class=&quot;internal new&quot; href=&quot;/articles/building-a-webapp-in-rust/&quot;&gt;here&lt;/a&gt;, &lt;a class=&quot;internal new&quot; href=&quot;/articles/webapp-localization-in-rust/&quot;&gt;here&lt;/a&gt;, and &lt;a class=&quot;internal new&quot; href=&quot;/articles/rendering-emails-with-svelte/&quot;&gt;here&lt;/a&gt;.
Let’s try to milk this project some more, this time with Rust and Astro.&lt;/p&gt;
&lt;p&gt;The entire website for JustFax &lt;del&gt;is&lt;/del&gt; was fully server rendered.
Like in the old times.
You would hit a URL, and the server would respond with an HTML.
Interactivity was handled by &lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt; and a bunch of vanilla JavaScript.&lt;/p&gt;
&lt;h2 id=&quot;the-problems-with-server-side-rendering&quot;&gt;The problems with server side rendering&lt;/h2&gt;
&lt;h3 id=&quot;templates-are-hard-to-maintain&quot;&gt;Templates are hard to maintain&lt;/h3&gt;
&lt;p&gt;Firstly, let’s not confuse Server Side Rendering (SSR) and Server Side Generation (SSG).
The former renders the HTML on demand per request, while the latter generates HTML files beforehand to be served by a server.&lt;/p&gt;
&lt;p&gt;I was using the &lt;a href=&quot;https://docs.rs/tera/latest/tera/&quot;&gt;tera&lt;/a&gt; templating engine.
Tera works with a format similar to Jinja.
Overall, the syntax is nice, and tera is great.
I was even able to implement hot reloading of templates, so I could run the rust server and edit the HTML, without the need to restart the server.&lt;/p&gt;
&lt;p&gt;However, as the project grew bigger, it became harder to maintain the HTML templates and, more importantly, the context I would pass to the templates.
Templates need variables.
Variables are passed from Rust to the tera engine by means of serialization (if I’m not mistaken via &lt;code&gt;serde_json&lt;/code&gt;).
In reality, they are completely detached from each other.
And this brings me to the first issue of server side rendering: errors in rendering the templates are &lt;strong&gt;run time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This means that if I misspelled a variable, the template would fail to render.
Refactoring code, and renaming variables—became a nightmare.
I needed to check each page manually before committing to deploying a new version.
I was considering switching to &lt;a href=&quot;https://github.com/djc/askama&quot;&gt;askama&lt;/a&gt;, which is a type-safe, Jinja-like, compiled template engine.
But eventually decided to generate all the HTML beforehand.&lt;/p&gt;
&lt;p&gt;In addition to that, Jinja, or at least the way it’s implemented in tera, is very limiting.
I would end up defining variables that are used purely for HTML purposes, in Rust, because I wasn’t able to achieve the same inside a template.
Examples include scenarios like: converting a vector of structs into JSON; and various translation shenanigans.
This is in no way a criticism towards tera; tera is a great templating engine, and I would definitely use it again in the future (if askama won’t prove to be better).
I never worked with Jinja or Django, so I assume these might be limitations of the templating language itself, rather than the tool.&lt;/p&gt;
&lt;p&gt;Oh, and one last annoyance.
Jinja is poorly supported in vim.
So I had to fight the editor with stupid errors.
One great example is from this piece of code:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; country&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; defined&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; country&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; INITIAL_COUNTRY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;{{country}}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; else&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; LANG&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; INITIAL_COUNTRY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;us&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; elif&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; LANG&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;de&quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; INITIAL_COUNTRY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;de&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; elif&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; LANG&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;fr&quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; INITIAL_COUNTRY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;fr&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; endif&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; %&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; endif&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The LSP would yell at me for trying to re-declare &lt;code&gt;INITIAL_COUNTRY&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;everything-is-a-run-time-problem&quot;&gt;Everything is a run-time problem&lt;/h3&gt;
&lt;p&gt;With server side templates, everything becomes a run-time problem.
Forgot to declare a variable?
Run time errors.
Extending a base template that does not exist?
Run time errors.&lt;/p&gt;
&lt;p&gt;On a small website with 2–3 pages, it’s easy to check each page manually.
But at some point it becomes too complicated, and I wish I could outsource this to some build step.
Maybe compiled templates like askama would help here, but I haven’t tried them.&lt;/p&gt;
&lt;p&gt;In addition to that, maintaining SSR requires a lot of tools.
Rust is amazing in run-time, but not that in compile-time.
Removing tera and other crates that I used that helps with template rendering—improved the compile time of the project.
Previously, it would take about 4 minutes to compile the rust code in CI.
After removing tera, markdown related crates, and other crates used for formatting (for example phone number formatting)—I was able to reduce the compilation time to 3 minutes, which might not sound like a lot, but this is a great improvement.&lt;/p&gt;
&lt;p&gt;In the end, if I look at it, the HTML was completely static.
There was no need to re-render it for each request, and the dynamic parts were small enough to be able to generate them in the browser without incurring any performance penalty.
I also learned the hard way that Rust is way behind JavaScript in terms of l10n and i18n.
While it has great support for translations (I personally like &lt;a href=&quot;https://projectfluent.org/&quot;&gt;project fluent&lt;/a&gt; by Mozilla)—it has no crates to format dates/numbers/currencies.
There are crates like &lt;a href=&quot;https://crates.io/crates/icu&quot;&gt;icu&lt;/a&gt; that are in development, that essentially tap into the Common Locale Data Repository (&lt;a class=&quot;internal new&quot; href=&quot;/articles/a-different-approach-for-localizing-react-js-app/&quot;&gt;I wrote about CLDR back in 2015&lt;/a&gt;).
This would allow Rust to have the same capabilities as JavaScript has through the &lt;code&gt;Intl&lt;/code&gt; API, but currently it’s still a work-in-progress.&lt;/p&gt;
&lt;p&gt;And so, I decided to turn to my second favorite tool.&lt;/p&gt;
&lt;h2 id=&quot;hello-astro-naut&quot;&gt;Hello Astro-naut&lt;/h2&gt;
&lt;p&gt;I’m a big fan of Astro, and in fact this blog is powered by Astro.
Astro, unlike other frameworks, does not force you to use a particular frontend framework, you can use vanilla JavaScript or TypeScript, or you can mix them all together.
It’s fast enough to generate hundreds of pages.
It comes with a lot of tools for websites like automatic sitemap generation, assets optimization and bundling, etc.&lt;/p&gt;
&lt;p&gt;Most of the issues I described above are also solved by Astro.
Template data and representation, are connected, especially if you opt for TypeScript.
It will be impossible to generate a page that includes a non-existing Astro component, for example.
Astro comes with superb support for Markdown content collection, which makes it super easy to generate content.
When I was using Rust, I had to sort-of reimplement what Astro does.
It didn’t work well.&lt;/p&gt;
&lt;p&gt;L10n and i18n are also solved with Astro.
You can pick any translation library you want, including project fluent from Mozilla.
And you have access to &lt;code&gt;Intl&lt;/code&gt; for formatting numbers, dates, and currencies.&lt;/p&gt;
&lt;p&gt;The only problem with Astro is that it’s a static site generator.
&lt;strong&gt;Wait? What?!&lt;/strong&gt;
Yes, I know I said that I wanted to switch to SSG from SSR, how come it’s a downside?&lt;/p&gt;
&lt;p&gt;Well, as I described in this blog post—&lt;a class=&quot;internal new&quot; href=&quot;/articles/webapp-localization-in-rust/&quot;&gt;Web app localization in Rust&lt;/a&gt;—I need a server in order to do locale negotiation.
Before rendering the initial HTML, I need to query cookies and the &lt;code&gt;Accept-Language&lt;/code&gt; header, and determine the preferred language of the visitor.
This can be done with JavaScript, for example by using the &lt;code&gt;window.navigator.languages&lt;/code&gt; property (which &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages&quot;&gt;MDN&lt;/a&gt; claims to be the same values as &lt;code&gt;Accept-Language&lt;/code&gt; header).
However, this would harm usability, as Google won’t be able to index the website correctly.&lt;/p&gt;
&lt;p&gt;Astro, does, however take care of this as well.
If you decide to opt for &lt;code&gt;hybrid&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt; output for your Astro website, you will gain access to &lt;code&gt;Astro.preferredLocale&lt;/code&gt; and &lt;code&gt;Astro.preferredLocaleList&lt;/code&gt; properties (as describe in the &lt;a href=&quot;https://docs.astro.build/en/guides/internationalization/#browser-language-detection&quot;&gt;Internalization article&lt;/a&gt; from Astro Docs).
But opting in for &lt;code&gt;hybrid&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt; outputs creates a problem.
You are now locked into either using Netlify or Vercel edge functions (which has official Astro adapters), or go back to Node.
Neither of them I wanted to do.
I have my locale negotiation code in Rust, I want it to stay in Rust.
In addition to that, the locale negotiation by Astro does not take into account the cookie that I set in order to avoid sending an English speaking German person to the German version of the website, even though he insisted for English interface, unlike Google is doing.&lt;/p&gt;
&lt;p&gt;And so, I was left with the only option, serve Astro website with Rust.&lt;/p&gt;
&lt;h2 id=&quot;serving-astro-website-with-rust&quot;&gt;Serving Astro website with Rust&lt;/h2&gt;
&lt;p&gt;Astro generates, by default, your entire website structure in the &lt;code&gt;dist&lt;/code&gt; folder.
HTML pages as well as additional resources will be there.
All is left, is just to expose this folder to the web.&lt;/p&gt;
&lt;p&gt;One approach I wanted to use, is to serve the website by &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;.
I already use Caddy as reverse-proxy for the Rust server, and Caddy is capable of serving folders.
However, figuring out the correct handlers order for Caddy—made my cabesa hurt.
You see, theoretically I could point &lt;code&gt;/&lt;/code&gt; to &lt;code&gt;/&lt;/code&gt; on Rust server; &lt;code&gt;/api&lt;/code&gt; to &lt;code&gt;/api&lt;/code&gt; on Rust server; &lt;code&gt;/en&lt;/code&gt; to &lt;code&gt;/dist/en&lt;/code&gt;; &lt;code&gt;/de&lt;/code&gt; to &lt;code&gt;/dist/de&lt;/code&gt;; etc.
But what do I do with static resources like JavaScript, sitemap, robots.txt, etc?
You see, it makes my head hurt.
So this option was out of the window.&lt;/p&gt;
&lt;p&gt;Let’s now talk about Rust.
I use &lt;a href=&quot;https://github.com/tokio-rs/axum&quot;&gt;axum&lt;/a&gt; as my web framework.
Axum is capable of serving directories using the &lt;code&gt;ServeDir&lt;/code&gt; service.
You give it a path to a directory, and it will serve the files from there.
So I have a regular axum route that handles the &lt;code&gt;/&lt;/code&gt; url; route that handle &lt;code&gt;/api&lt;/code&gt; url(s); and everything else is fallen back to &lt;code&gt;ServeDir&lt;/code&gt;.
This covers 99% of the files.
The last 1% are 404 files.
Astro will allow you to define &lt;code&gt;404.astro&lt;/code&gt; for a custom 404 page, and services like Netlify or Vercel will be able to route to it, but not axum.&lt;/p&gt;
&lt;p&gt;If axum fails to find a matching route for the request, nor it finds a static file inside the provided &lt;code&gt;ServeDir&lt;/code&gt; path, you are able to implement a custom 404 handler.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fallback_service&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ServeDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;public_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;append_index_html_on_directories&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;not_found_service&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Handler&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;with_state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handle_404&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;handle_404&lt;/code&gt; you can do all kinds of things.
I do locale negotiation to decide which version of the 404 page to serve, but eventually it boils down to&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;match&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ServeFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;as_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;try_call&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;empty&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) =&gt; &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;into_response&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) =&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;StatusCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;NOT_FOUND&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;into_response&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While &lt;code&gt;path&lt;/code&gt; is the path to &lt;code&gt;[en|de]/404/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another thing with Astro, is that it has a great asset management pipeline.
Each static resource will be processed by Astro and a hashed name file will be put it &lt;code&gt;_astro&lt;/code&gt; directory.
Since the hash of the file represents the hashed content, if the file did not change, it means that it will have the same hash.
And this means that &lt;code&gt;_astro&lt;/code&gt; is a great directory to cache aggressively.&lt;/p&gt;
&lt;p&gt;I had to fight a bit with axum in order to understand how to send headers alongside &lt;code&gt;ServeDir&lt;/code&gt; file, but eventually I was able to figure it out.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; public_path&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.server.www_dir);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fallback_service&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ServeDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;public_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;append_index_html_on_directories&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;not_found_service&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handle_404&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;into_service&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fallback&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; move&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;into_parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; uri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;OriginalUri&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extract&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; req&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from_parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fallback_service&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;oneshot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) =&gt; &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;                StatusCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;OK&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;                    if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; uri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;contains&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/_astro/&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;                        res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;headers_mut&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;                            &quot;Cache-Control&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;                            &quot;public, max-age=31536000&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;                    Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;                _&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;                tracing&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;error!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fallback_service error: {}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;                Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;layer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;service_layer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This essentially uses the &lt;code&gt;ServeDir&lt;/code&gt; service through the &lt;code&gt;oneshot&lt;/code&gt; request, and based on the request &lt;code&gt;uri&lt;/code&gt; it sets the cache headers for &lt;code&gt;_astro&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;That’s it.
That’s the blog post.&lt;/p&gt;</content:encoded></item><item><title>Rendering emails with Svelte</title><link>https://kudmitry.com/articles/rendering-emails-with-svelte/</link><guid isPermaLink="true">https://kudmitry.com/articles/rendering-emails-with-svelte/</guid><pubDate>Sat, 24 Feb 2024 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Emails play an important role in my project &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
Before I implemented emails, I would spend time answering customers’ requests for fax status updates.
This means that I had to monitor each fax transmission (and some can take up to 30 minutes) and send an email when it succeeded or failed.
And so the obvious solution was to implement email notifications.&lt;/p&gt;
&lt;p&gt;I hacked something together using MJML, handlebars, fluent for localization and &lt;a href=&quot;https://postmarkapp.com/&quot;&gt;Postmark&lt;/a&gt;.
Postmark is a great service by the way.
I’m not affiliated with them, but they have superb customer support.
I tweeted this:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-theme=&quot;dark&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;My wish for the new year, is that &lt;a href=&quot;https://twitter.com/postmarkapp?ref_src=twsrc%5Etfw&quot;&gt;@postmarkapp&lt;/a&gt; will approve my account today 🙃&lt;br&gt;&lt;br&gt;And then, I’ll be able to ship something cool!&lt;/p&gt;— Dmitry Kudryavtsev (@skwee357) &lt;a href=&quot;https://twitter.com/skwee357/status/1740671513634943473?ref_src=twsrc%5Etfw&quot;&gt;December 29, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;And after 7 minutes they approved my account.
Highly recommend!
But let’s talk about emails.&lt;/p&gt;
&lt;h2 id=&quot;tables-tables-everywhere&quot;&gt;Tables. Tables everywhere.&lt;/h2&gt;
&lt;p&gt;If you ever had a chance to design emails, you know that it’s one of the most terrible jobs to do.
Emails are still designed using HTML tables, and getting them right on every client—is an impossible task.
Luckily, there are tools like &lt;a href=&quot;https://mjml.io/&quot;&gt;MJML&lt;/a&gt; which provide a DSL similar to HTML in order to design your emails.
With compilers available for all major languages, it’s a great solution for responsive emails.
Of course for the hip-and-cool there are &lt;a href=&quot;https://react.email/&quot;&gt;React.email&lt;/a&gt;, &lt;a href=&quot;https://vuemail.net/&quot;&gt;VueEmail&lt;/a&gt;, and &lt;a href=&quot;https://svelte-email.vercel.app/docs/overview/svelte-email&quot;&gt;Svelte Email&lt;/a&gt;.
But I chose MJML.&lt;/p&gt;
&lt;p&gt;In addition to MJML, I used handlebars as a lightweight templating language.
Emails rarely need complex logic, so handlebars with its simple syntax was a perfect choice.
JustFax is available currently in 3 languages: English, German, and French.
This means that emails need to be available in the same languages.
I use &lt;a href=&quot;https://projectfluent.org/&quot;&gt;Fluent&lt;/a&gt; by Mozilla as my i18n framework through a set of crates for Rust: the low level &lt;a href=&quot;https://crates.io/crates/fluent&quot;&gt;fluent&lt;/a&gt;, and the higher level &lt;a href=&quot;https://crates.io/crates/fluent-templates&quot;&gt;fluent-templates&lt;/a&gt; which was designed specifically to be used in template engines like tera and handlebars.&lt;/p&gt;
&lt;p&gt;MJML files would sit in the same monorepo as the rest of the code, and through the MJML npm package, they will be compiled to HTML in CI/CD.
In runtime, Rust will load the HTML files, initialize Fluent and handlebars, and upon successful (or failed) fax delivery—would send the correct email.
This was good enough for the MVP, but it had some problems I wanted to improve on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Preview&lt;/strong&gt;.
Previewing emails was hard.
While the MJML tool comes with an online app (and a one you can download), frequent readers of this blog know that I’m stuck in Vim and I won’t use any other editor.
But even if you decide to use MJMLs own tool, or the live editor, you still can’t preview handlebars templates.
So the entire process of previewing your emails would turn into a nightmare of copy-pasting stuff around.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Modification&lt;/strong&gt;.
Email modification is another thing that was hard.
Because there was no proper preview, modifying the emails became a risky task.
I would either break some variable, misspell a translation key, or would run into handlebars compilation errors, which happened in runtime.&lt;/p&gt;
&lt;p&gt;Armed with the desire to improve these two problems, I started to look for solutions.&lt;/p&gt;
&lt;h2 id=&quot;attempt-01compiled-templates-for-rust&quot;&gt;Attempt #01—compiled templates for Rust&lt;/h2&gt;
&lt;p&gt;Rust has this notion of compiled templates.
There are two popular crates for this: &lt;a href=&quot;https://crates.io/crates/askama&quot;&gt;askama&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/yarte&quot;&gt;yarte&lt;/a&gt;.
The former is for lovers of Jinja syntax, while the latter is better suited for handlebars/mustache enthusiasts.
The idea behind compiled templates is that through macro programming, they will analyze the template code and extract all the needed variables to a generated struct.
This means that you can’t accidentally forget to specify a variable or mistype it.
If you do, your code won’t compile.&lt;/p&gt;
&lt;p&gt;I tried yarte, but it was somewhat slow, and I got many errors with it.
On top of it, while yarte solves the type-safety aspect of templates, it does not compile MJML to html, or allow me to preview my templates.
And after some brainstorming, and inspired by the recent change I made for the frontend (article is coming, drop a follow on &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;https://mstdn.social/@skwee357&quot;&gt;Mastodon&lt;/a&gt;, or subscribe to the newsletter—to make sure you won’t miss it), I asked my self “&lt;em&gt;what would happen if I try to generate emails statically, like with static site generators?&lt;/em&gt;”.&lt;/p&gt;
&lt;h2 id=&quot;attempt-02statically-generated-email-templates&quot;&gt;Attempt #02—statically generated email templates&lt;/h2&gt;
&lt;p&gt;What if we take the same principles behind static site generators like Astro or SvelteKit, and use them to generate emails?&lt;/p&gt;
&lt;h3 id=&quot;vite&quot;&gt;Vite&lt;/h3&gt;
&lt;p&gt;My first attempt was using Vite.
If you put all your templates in a Vite project, add some plugins—namely &lt;a href=&quot;https://www.npmjs.com/package/vite-plugin-mjml&quot;&gt;vite-plugin-mjml&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/package/vite-plugin-handlebars&quot;&gt;vite-plugin-handlebars&lt;/a&gt;—you can achieve an amazing result!&lt;/p&gt;
&lt;p&gt;Inside your Vite config, you can define the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; defineConfig&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  plugins&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    mjml&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      output&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;emails&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      extension&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      watch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    handlebars&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will take all files inside &lt;code&gt;src&lt;/code&gt; directory, pass them through MJML compiler and put the result with &lt;code&gt;.html&lt;/code&gt; extension inside &lt;code&gt;emails&lt;/code&gt; directory.
It will also watch for changes, so if you change the template, you will get hot reloading.
Navigating then to &lt;code&gt;localhos:4321/emails/template_name.html&lt;/code&gt; will render the compiled MJML in the browser.
Not bad!
But if you actually try this, you will see that handlebars variables are all empty.&lt;/p&gt;
&lt;p&gt;We can do better.
Using the handlebars plugin, we can define &lt;code&gt;pageData&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; pageData&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &quot;/emails/template_name.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    someVar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;someValue&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    foo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;handlebars&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; pageData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we go to the same path again now, we will see that our handlebars variables are properly replaced with the provided data from &lt;code&gt;pageData&lt;/code&gt;!
Specifying &lt;code&gt;strict: true&lt;/code&gt; inside the handlebars plugin, will allow us to see errors about any missing variables from the template.&lt;/p&gt;
&lt;p&gt;This is good enough for most use cases.
But not for me!
I wanted more.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ability to view the email in different languages&lt;/li&gt;
&lt;li&gt;Ability to modify the email data&lt;/li&gt;
&lt;li&gt;Ability to preview email in desktop and mobile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And so, I embarked on my next journey—SvelteKit.&lt;/p&gt;
&lt;h3 id=&quot;sveltekit&quot;&gt;SvelteKit&lt;/h3&gt;
&lt;p&gt;Everything I described above requires a bit more than just static HTML served in a browser.
It requires some sort of server and logic.
And SvelteKit allows for that.
I’ll first put a small gif of the resulting product, and then I’ll walk you through the important steps of how to achieve this.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Email preview with MJML, handlebars, and SvelteKit&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;363&quot; src=&quot;/_astro/email_preview.DP1gQf_6_1msy96.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Email preview with MJML, handlebars, and SvelteKit&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Since SvelteKit is based on Vite, the build process remains as described above with the only exception that we no longer need the handlebars plugins.
Having the MJML plugin will allow the build process to compile MJML into HTML, which I then can render in Rust with handlebars.
During CI/CD, the resulting directory that contains the HTML of the emails, is simply copied into the Docker container, and read by Rust upon startup.
However, in order to achieve the preview and navigation, we will have to implement some routes.&lt;/p&gt;
&lt;p&gt;We will need two route: ’/’ and ‘/render’ with the last being server only route.&lt;/p&gt;
&lt;h4 id=&quot;main-route-&quot;&gt;Main route ’/’&lt;/h4&gt;
&lt;p&gt;The purpose of this route is mainly UI.
It should display a list of email templates, and allow the user to select a particular template.
It consists of two files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;+page.server.ts&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;PageServerLoad&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;./$types&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; load&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PageServerLoad&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; files&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;glob&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;../../mjml/*.mjml&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;raw&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; fileNames&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;files&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;replaceAll&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;../../mjml/&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;emails&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileNames&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing special going on there.
We just import all the &lt;code&gt;*.mjml&lt;/code&gt; files, clean their names, and return them to the page.&lt;/p&gt;
&lt;p&gt;The other file is &lt;code&gt;page.svelte&lt;/code&gt;, but I won’t include it here since it’s mostly UI.
The important bit is to declare data import &lt;code&gt;export let data: PageData;&lt;/code&gt; so that the result of the &lt;code&gt;load&lt;/code&gt; function will be available to the page.
After that, just render a button for each email template:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;svelte&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{#&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;emails&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;click&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; sendRequest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {})&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;      class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`flex items-center font-medium gap-2 justify-between w-full my-1 borer border-gray-400 rounded-md hover:bg-gray-200 px-4 py-2 &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;activeEmail&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; email&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;bg-gray-200&apos;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        {&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{/&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might have noticed the &lt;code&gt;sendRequest&lt;/code&gt; callback, let’s talk about it.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; sendRequest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  activeEmail&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  fetch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/render&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    method&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      html&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      content&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      validator&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; createAjvValidator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      updateFrameContent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Upon selection of the email template, we will issue a POST request to &lt;code&gt;/render&lt;/code&gt; with the name of the template.
The request is the name of the file, and a &lt;code&gt;context&lt;/code&gt; object which will be used to render the template by handlebars.
Initially, the &lt;code&gt;context&lt;/code&gt; object is empty.&lt;/p&gt;
&lt;p&gt;The response is a JSON that contains the parsed and rendered HTML; an updated &lt;code&gt;context&lt;/code&gt; object; a JSON schema &lt;code&gt;schema&lt;/code&gt;; and &lt;code&gt;errors&lt;/code&gt; object that MJML produced.
I ended up not using the &lt;code&gt;errors&lt;/code&gt;, as I achieved what I needed, but in theory you can create a nice validation to inform the frontend that your MJML template is broken.&lt;/p&gt;
&lt;h4 id=&quot;render-route-render&quot;&gt;Render route &lt;code&gt;/render&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The render endpoint is purely server side, and resides inside a &lt;code&gt;/render/+server.ts&lt;/code&gt; file.
I’ll dump the code first, and then we will discuss the important parts.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; RequestHandler&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;@sveltejs/kit&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fs&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;node:fs&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; mjml2html&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;mjml&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Handlebars&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;handlebars&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;getTranslationFunction&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../../i18n&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Ajv&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; AnySchema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;ajv&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; RequestBody&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  context&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Record&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ajv&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Ajv&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;useDefaults&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; POST&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;RequestHandler&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;RequestBody&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; mjml&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;readFileSync&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`./mjml/&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; mjml2html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;mjml&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;readFileSync&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`./schemas/&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;replace&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;.mjml&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; validate&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ajv&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;compile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AnySchema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; context&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ??&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;validate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;errors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;validate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;errors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }), {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      status&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        &quot;Content-Type&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getTranslationFunction&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;??&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  Handlebars&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;registerHelper&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fluent&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; tpl&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Handlebars&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;compile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; output&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; tpl&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    JSON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;output&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      schema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      errors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;errors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      status&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        &quot;Content-Type&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code accepts a JSON object with &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;.
&lt;code&gt;file&lt;/code&gt; is a path to a template file inside the &lt;code&gt;mjml&lt;/code&gt; folder (or any other folder name you decide to store your MJML files in).
&lt;code&gt;context&lt;/code&gt; is an object to render handlebars with.&lt;/p&gt;
&lt;p&gt;The code first reads the template and parses the MJML into HTML.
It then reads a &lt;code&gt;json&lt;/code&gt; file from &lt;code&gt;schemas&lt;/code&gt; folder that have the same name as the template file.
This JSON file contains a JSON schema.
This is the cool part, because this file acts as both documentation and for initial preview purposes.
I can use this JSON schema as a reference in order to define the corresponding Rust struct, or if I want to take it further, I can add a compile step that will generate the struct for me during build based on the schema.
At the moment, I do it manually.&lt;/p&gt;
&lt;p&gt;Next, I compile the JSON schema and validate it.
Since &lt;code&gt;ajv&lt;/code&gt; is created with &lt;code&gt;useDefaults: true&lt;/code&gt;, and all fields in the schema will have default values, this gives me the initial &lt;code&gt;context&lt;/code&gt; object when I send &lt;code&gt;{}&lt;/code&gt; in the first request.
I then get my &lt;code&gt;t()&lt;/code&gt; function which is used for translation, and bound to the &lt;code&gt;context.lang&lt;/code&gt; or &lt;code&gt;en&lt;/code&gt;.
Since returning the &lt;code&gt;t()&lt;/code&gt; requires promises to load the &lt;code&gt;.ftl&lt;/code&gt; files and initialize &lt;code&gt;FluentBundle&lt;/code&gt;, I had to redeclare handlebars &lt;code&gt;fluent&lt;/code&gt; helper for each request, as handlebars does not support asynchronous helpers.
It’s an MVP in the end ;)&lt;/p&gt;
&lt;p&gt;Lastly, I compile the template with handlebars and send back the response, together with the schema, the updated context, and any errors from MJML.&lt;/p&gt;
&lt;h4 id=&quot;tying-it-up-together&quot;&gt;Tying it up together&lt;/h4&gt;
&lt;p&gt;I won’t bore you with the frontend code, but I do want to mention some tools I used.&lt;/p&gt;
&lt;p&gt;The emails’ HTML is rendered inside an iframe.
Every time there is a new response, I call this function which updates the iframe content:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; updateFrameContent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    frame&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;about:blank&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    frame&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;contentWindow&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;open&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    frame&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;contentWindow&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    frame&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;contentWindow&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;frame&lt;/code&gt; is a variable that is bound to the iframe: &lt;code&gt;&amp;#x3C;iframe class=&quot;w-full bg-white h-full mx-auto&quot; title=&quot;email&quot; bind:this={frame}&gt;&amp;#x3C;/iframe&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;context&lt;/code&gt; preview and modification, I used &lt;code&gt;svelte-jsoneditor&lt;/code&gt; packages.
It has a lot of features, and even able to validate the JSON using JSON schema, which ties everything together nicely.
I listen to &lt;code&gt;onChange&lt;/code&gt; event of the &lt;code&gt;svelte-jsoneditor&lt;/code&gt;, and upon every correct JSON, I reissue the request:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;svelte&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;onChange=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;json&apos;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;object&apos;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;        onContextChange&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;text&apos;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;            const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; json&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            onContextChange&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are some UI things to handle resizing the iframe from full width to 360px for mobile view, and another button to replace the iframe with a &lt;code&gt;Highlight&lt;/code&gt; component from &lt;code&gt;svelte-highlight&lt;/code&gt; for the HTML source code.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I’m very happy with this solution!
I’m able to preview the emails with all kinds of different values.
It’s even possible to create dynamic svelte forms based on the JSON schema, so if your template can accept an enum, you can have a drop-down list of all the possible values.&lt;/p&gt;
&lt;p&gt;I’m also able to view emails in different languages, something that I previously was able to do only after the emails were sent, using Postmark dashboard.
The JSON schema acts as a documentation to create proper types in Rust backend, and can be further processed in order to automate types creation.
The only downside of this approach is that during build, the entire boilerplate is being built as well.
In my CI/CD pipeline I copy only the resulting templates’ HTML, but nevertheless I spend computing power to generate code I don’t need.
I bet it’s possible to configure Vite with some option or flag in order to only run the MJML plugin, but I didn’t bother with it since this process runs in parallel to Rust compilation, and that takes way more time that generating the HTML templates and everything else.&lt;/p&gt;
&lt;p&gt;Emails are a pain in the butt to work with.
I’m happy that we have tools like MJML, and I’m glad to share my knowledge and, hopefully, help you improve your email workflow.&lt;/p&gt;</content:encoded></item><item><title>How to stay junior forever</title><link>https://kudmitry.com/articles/how-to-stay-junior-forever/</link><guid isPermaLink="true">https://kudmitry.com/articles/how-to-stay-junior-forever/</guid><pubDate>Tue, 30 Jan 2024 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my &lt;a class=&quot;internal new&quot; href=&quot;/articles/bloat-in-software-engineering/&quot;&gt;last blog post&lt;/a&gt;, I mentioned briefly the fact that today there are more engineers than meaningful jobs for them.
This means that there is an immense pressure on junior engineers.
Getting a job becomes a challenge.&lt;/p&gt;
&lt;p&gt;The other day I stumbled upon a post in &lt;a href=&quot;https://reddit.com/r/programming&quot;&gt;/r/Programming&lt;/a&gt;.
The post was talking about how GitHub Copilot affects code quality.
You can read the post &lt;a href=&quot;https://visualstudiomagazine.com/articles/2024/01/25/copilot-research.aspx&quot;&gt;here&lt;/a&gt;.
The only important quote from the post is this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;We find disconcerting trends for maintainability. Code churn — the percentage of lines that are reverted or updated less than two weeks after being authored — is projected to double in 2024 compared to its 2021, pre-AI baseline. We further find that the percentage of ‘added code’ and ‘copy/pasted code’ is increasing in proportion to ‘updated,’ ‘deleted,’ and ‘moved ‘code. In this regard, AI-generated code resembles an itinerant contributor, prone to violate the DRY-ness [don’t repeat yourself] of the repos visited.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This reminded me of something.&lt;/p&gt;
&lt;h2 id=&quot;copy-paste&quot;&gt;Copy-paste&lt;/h2&gt;
&lt;p&gt;I remember when I joined Autodesk, back in 2017, I got the experience to work together with different kinds of engineers.
Prior to Autodesk, I worked mostly in small teams with little to no supervision.
But at Autodesk I had responsibilities such as mentoring other engineers, as well as conducting pair-programming sessions.
These practices allowed me to take a glimpse into how other engineers work and write code.&lt;/p&gt;
&lt;p&gt;And I noticed that many engineers write code in, what I call, a copy-paste method.
They would copy blocks of their own code around, and modify the necessary parts.
Moreover, they would copy code snippets from Stackoverflow or other online resources.&lt;/p&gt;
&lt;p&gt;I don’t practice copy-paste programming.
Don’t get me wrong, I do copy and paste parts of my own code, as well as code found on the internet.
But I never do it for code that I don’t understand.
And this made me wonder, am I just being inefficient?
It took more than 6 years to finally find the answer.&lt;/p&gt;
&lt;h2 id=&quot;the-power-of-writing&quot;&gt;The power of writing&lt;/h2&gt;
&lt;p&gt;Last year I published one of my all times popular blog posts—&lt;a class=&quot;internal new&quot; href=&quot;/articles/why-engineers-should-write/&quot;&gt;Why engineers should focus on writing&lt;/a&gt;.
It even reached the top page of HackerNews!
One particular advice, that was copied into multiple publications, and mentioned by multiple people on social networks, was:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;One last advice–abolish the copy-paste. So many developers, whom I mentored, simply copy-paste everything. Code snippets, function declarations, etc. I know how to initialize a git repository, because I do it by hand every time. Most people simply copy the instructions from GitHub or Google. And if you are scared about being unproductive–remember that you are not judged by how much code you write, or how fast you complete the assignment.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;There is a reason why writing is a skill and not a habit.
Habit is something that you do repetitively.
You can’t perfect a habit, you just stay consistent for as long as possible.
Skill, on the other hand, is something you can perfect.
And perfection of skills is achieved by doing them over and over again, with slight improvement over time.&lt;/p&gt;
&lt;p&gt;Software engineering is a skill, and in order to perfect it you have to write more code.
The keyword is &lt;strong&gt;write&lt;/strong&gt;.
Not copy.
Not generate.
Write.&lt;/p&gt;
&lt;p&gt;It might sound counter-intuitive, because one could argue: &lt;em&gt;why should I spend time &lt;strong&gt;typing&lt;/strong&gt;, while I can just copy and paste?&lt;/em&gt;
And it’s a valid argument, but copy-pasting code is the same as watching how to drive a race car—it won’t make you into a race car driver.
I know the things I know because I keep writing them over and over again.
That’s why I hate all these &lt;code&gt;gp&lt;/code&gt; and other shortcuts that people make for git commands.
I type &lt;code&gt;git push&lt;/code&gt; every time.&lt;/p&gt;
&lt;p&gt;Not only it engraves these commands into my brain, so I won’t have to Google them every time, it also does something with the way I understand things.
I can’t explain in, and it frustrates me because I’m a logical person, but writing is magical.
Any type of writing.
This is why students are asked to take notes because that’s how you learn.
And that’s why good engineers write.
Code, and blogs, and articles.
The first advice we ever give to anyone who is struggling to solve a problem is: write it down, draw it on the whiteboard.&lt;/p&gt;
&lt;p&gt;And this leads me to the main topic of this post.&lt;/p&gt;
&lt;h2 id=&quot;how-to-stay-junior-forever&quot;&gt;How to stay junior forever&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;If you want to stay junior forever, keep using AI and other code assisting tools.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s a bold statement, but I’m willing to stand behind it.
The more you use AI, Copilots, and other helpers alike—the less of an engineer you become, and the easier it is to replace you with AI.
Software engineering is not only writing code.
In fact, software engineers is everything except writing code.
Writing code is the easiest part.
Once you know 2–3 families of programming languages, all the rest are the same, just different syntax.&lt;/p&gt;
&lt;p&gt;However, translating requirements into code, and more importantly understanding existing code—this is what software engineering is about.
Writing code is the mechanical part of your hands.
But in order to write it, you need a pre-trained brain that can translate the problem into code.&lt;/p&gt;
&lt;p&gt;Now, it’s not that I’m against AI and code assisting tools.
In the end, we all went up from using Notepad to using auto-completion and intelli-sense.
But none of the pre-AI tools are crucial.
They help me to remember what are the arguments for &lt;code&gt;Array.slice&lt;/code&gt; function, but they do not replace my ability to understand what a given piece of code does.&lt;/p&gt;
&lt;p&gt;With copy-paste and AI-like tools—it becomes too easy to abuse it.
The best way to avoid eating sweets, is not to keep sweets at home.
We are all humans, and it’s tempting to abuse what makes our life easier or brings us pleasure.
Seneca used to say:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This is what I mean: pleasure, unless it has been kept within bounds, tends to rush headlong into the abyss of sorrow.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;But the thing is, like with eating a piece of chocolate a day, you will pay for it in the future.
And instead of becoming an engineer to whom colleagues will come for advice—you will be replaced by the same tool you adored, because you practiced the habit of typing, rather than mastering the skill of software engineering.&lt;/p&gt;
&lt;p&gt;Farewell.&lt;/p&gt;</content:encoded></item><item><title>The Bloat in Software Engineering</title><link>https://kudmitry.com/articles/bloat-in-software-engineering/</link><guid isPermaLink="true">https://kudmitry.com/articles/bloat-in-software-engineering/</guid><pubDate>Tue, 23 Jan 2024 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been thinking recently about two things: The Roman Empire and the unnecessary bloat in Software Engineering.
You see, about 7 months ago &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;I quit my job due to burnout&lt;/a&gt;.
Since then, I spent a lot of time thinking and analyzing what is so wrong with our industry.
I wrote once about &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-software-industry-is-broken/&quot;&gt;how our industry is broken&lt;/a&gt;, but that article was referring more to the technical side and code bloat.
Today, I want to talk about the cultural and architectural sides.&lt;/p&gt;
&lt;h2 id=&quot;lets-build-a-kafka-queue-with-distributed-microservices-architecture&quot;&gt;Let’s build a Kafka queue with distributed microservices architecture&lt;/h2&gt;
&lt;p&gt;Seems like the microservices architecture hype is finally over.
People are coming back to traditional monoliths, or not caring at all about labeling their architecture.
But there still remains a trend that many follow.
I call it “the MANGA trend”.&lt;/p&gt;
&lt;p&gt;Inexperienced engineers, or engineers that are driven by ego/desire to be promoted (and sometimes boredom, I’ll talk about it later), come up with one solution for every problem.
The “Kafka solution”.
Kafka, in this case, is just a placeholder for “your favorite MANGA tech stack” (MANGA: Meta, Apple, Netflix, Google, Amazon—The holy quintet of software engineering).&lt;/p&gt;
&lt;p&gt;What happens is that one of these “big tech” companies post on their super cool engineering blog, about a super hard problem they had, and how they solved it by building a reverse-proxy-on-top-of-a-potato-using-ipv6-over-telephone-wires, which is now open-source, and all inexperienced or ego driven engineers be like: “Wow! I want that too!”&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/much-wow.webp&quot; width=&quot;400&quot; height=&quot;400&quot; alt=&quot;Doge wow, such architecture, much wow&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Much wow. Such architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So they convince their fellow teammates, team leads, and engineering managers (using their superior writing skills which they acquired thanks to my recent book &lt;a class=&quot;internal new&quot; href=&quot;/books/technical-writing-for-software-engineers/&quot;&gt;Technical Writing for Software Engineers&lt;/a&gt;)—that we, too, need to have such architecture.
&lt;em&gt;It’s good for Google, why would it be bad for us?&lt;/em&gt;
Their plan could have been bulletproof, and would lead them to promotion, if only they didn’t forget one thing: they are not Google, and they have 12 MAU (monthly active users).&lt;/p&gt;
&lt;p&gt;The desire to implement over-engineered solutions is caused by multiple factors.
The need for social validation is one of them.
We want to be as cool and as trendy as the other company.
But we forget that we are not Google.
We don’t have the same scale, and definitely not solving the same problem.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;It’s interesting because this trend is sort of catch-22. From the employee side, many engineers want to work in companies that work with the same tech as MANGA, so they would ask these question during the interviewing process. But from the employer side, the drive towards Cool Tech might be fueled by said potential employee, as well as internal desire inside the engineering team to adopt Cool Tech.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And then, you will meet the “anti-Donald” type of developers.
In a paper titled &lt;a href=&quot;https://pic.plover.com/knuth-GOTO.pdf&quot;&gt;“Structured programming with go to statements”&lt;/a&gt;, Donald E. Knuth famously wrote:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: &lt;strong&gt;premature optimization is the root of all evil.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;But then comes the “anti-Donald” developer who says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;We need to optimize for future. Our product is going to grow, and we need to support its future growth.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And while this statement by itself is not wrong (assuming it’s backed by actual data) there is another fallacy that comes attached to it.
The fallacy that says that the existing tech we use—is not scalable.
Which most of the time is wrong.
The following statements will piss-off many engineers, so be warned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.epicweb.dev/why-you-should-probably-be-using-sqlite&quot;&gt;Majority of the web could run on SQLite&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adriano.fyi/posts/2023-09-24-choose-postgres-queue-technology/&quot;&gt;PostgreSQL can be used as a queue&lt;/a&gt; without the need to introduce a standalone broker.&lt;/li&gt;
&lt;li&gt;And your bottleneck is not in your asynchronous processing technology.
It is, in fact, in your managerial inefficiency, and technological bloat.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;all-aboard-the-hyper-growth-train&quot;&gt;All aboard the hyper-growth train&lt;/h2&gt;
&lt;p&gt;There is a hard truth that needs to be said: there are more developers today than &lt;strong&gt;meaningful&lt;/strong&gt; jobs for them.
Here, I said it, you can cancel me now.&lt;/p&gt;
&lt;p&gt;Companies are living organisms.
And they have many of the same social interactions as humans have.
One such interactions is mirroring.&lt;/p&gt;
&lt;p&gt;When you, as a respecting and politically correct human being, find yourself in an environment of racists, you have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Leave&lt;/li&gt;
&lt;li&gt;Be racist (mirror your environment)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Yes, technically you have third option: to stand your ground and stay true to your principles.&lt;/em&gt;
&lt;em&gt;But com’on, I’m trying to make a point, so bear with me.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Companies rarely have the option to leave.
They all compete on the same market, over the same talent (most of the time).
And they all mirror each other.
This is why when Google announces layoffs, all the other MANAs will follow it.
And when one company releases an AI model, others will pivot to it as well.
And finally, when one enters a hyper-growth mode, others will follow.&lt;/p&gt;
&lt;p&gt;They do so for various reasons, which most of the time are unrelated to actually needing this talent for meaningful jobs.
One reason is mimicking what others are doing.
Another reason is to keep candidates off the market.
Amazon would rather hire all the best AI engineers, so Nvidia wouldn’t get them, even though Amazon had no plans to release a general purpose LLM.
But FOMO is real, and it’s better to insure myself then let a competitor get ahead.&lt;/p&gt;
&lt;p&gt;This creates a weird situation where a company is now stuck with personnel which has no actual work to do.
But nobody is getting paid to watch Instagram Reels all day, so we &lt;em&gt;have to&lt;/em&gt; come up with some sort of work.
And smart engineers, being smart, start to come up with over-engineered solutions.
This is why we end up with Kafka infested development in every company.&lt;/p&gt;
&lt;p&gt;In reality, it would be cheaper, and easier to implement Kafka when you are milking your resources to the max, instead of prematurely optimizing it.
And the reasons being is…&lt;/p&gt;
&lt;h2 id=&quot;legacy&quot;&gt;Legacy&lt;/h2&gt;
&lt;p&gt;I wrote about legacy code &lt;a class=&quot;internal new&quot; href=&quot;/articles/legacy-is-where-companies-go-to-die/&quot;&gt;in this blog post&lt;/a&gt;.
And the truth is that legacy code is unavoidable in our modern engineering culture.
Modern development practices prioritize endless feature list, while pushing technical backlog down to the abyss.
But technical backlog is the building block of avoiding legacy code.
You need to have constant refactoring sessions, and revisiting old code in order to avoid code rotting.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By the way, opposite scenario, where majority of the time is spent on refactoring, leads to another pandemic that I call “lets-rewrite-it-to-v2-using-new-cool-tech”.&lt;/em&gt;
&lt;em&gt;This also stalls healthy development and leads to over-engineered solutions that disregard any product features.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If instead of having a “premature-optimization” development cycles, we would concentrate on refactoring, you wouldn’t need to introduce Kafka “just-because”™.
But due to the desire to do cool stuff, “like Google does”, paired with the fact that a lot of the engineers are over-hired—we end up with premature optimization.
And in reality it always fails, because you are building for something you don’t have, hence you can’t know what do you need.
It would be wiser to introduce a particular technology when you hit a bottleneck and understand what you need to fix.&lt;/p&gt;
&lt;p&gt;The good news is that there seems to be a shift in the minds of developers and companies.
I see more and more people sharing how they abolish their overly complicated AWS setups in order to go back to traditional VPS + DevOps setups.
More engineers, myself included, are speaking about using simple technologies.
The above-mentioned blog post by Adriano Caloiaro about using PostgreSQL as queue is a great example.
The increased mentions of simple technologies such as SQLite and HTMX is another example.
So there is hope.&lt;/p&gt;</content:encoded></item><item><title>I spent €200 on Google Ads so you won&apos;t have to</title><link>https://kudmitry.com/articles/200-eur-for-google-ads/</link><guid isPermaLink="true">https://kudmitry.com/articles/200-eur-for-google-ads/</guid><pubDate>Thu, 11 Jan 2024 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you remember, in &lt;a class=&quot;internal new&quot; href=&quot;/articles/6-lessons-6-months-6-projects/&quot;&gt;one of my&lt;/a&gt; past blog posts, I was talking about the fact that I want to try running ads for &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
In general, I don’t like ads.
Something in the advertisement industry repels me.
However, as I mentioned in that blog post, for some products running an ad campaign can actually benefit.
And so I spent €200 on Google Ads, so you won’t have to.
Let’s see what happened.&lt;/p&gt;
&lt;h2 id=&quot;segment-properly&quot;&gt;Segment properly&lt;/h2&gt;
&lt;p&gt;The first time I ran the campaign, I did a mess.
I targeted multiple countries, with multiple speaking languages.
This led to a lot of irrelevant clicks.&lt;/p&gt;
&lt;p&gt;I wanted to target the US region, as well as some non-English-speaking countries.
So I created one campaign, in English, and set the target audience to US, and few other countries who speak English and non-English.
This turned out to be a bad decision as it’s preferably better to segment the audience with different campaigns.
Not only it lowers the cost (more on that in a moment), but also generates clicks that are more likely to convert.&lt;/p&gt;
&lt;p&gt;Second thing I didn’t take into account, is that the same keyword, but with different target audience—will have a different cost.
This is why it’s important to segment properly.&lt;/p&gt;
&lt;h2 id=&quot;think-about-acquisition-cost&quot;&gt;Think about acquisition cost&lt;/h2&gt;
&lt;p&gt;When users started to flow, I got so happy!
I had orders, made refunds, improved the system.
But I forgot one small thing—I pay more per click than I make from converted customers.&lt;/p&gt;
&lt;p&gt;This motivated me to try and lower the cost as much as possible.
Since I don’t operate on a recurring subscription model, this means that I either need to have a very tight budget, or explore other channels of customer acquisition.
Speaking about budget…&lt;/p&gt;
&lt;h2 id=&quot;on-daily-budget&quot;&gt;On daily budget&lt;/h2&gt;
&lt;p&gt;I’m not entirely sure how the whole advertisement system works, but from what I understand you bid for ad placement.
This means that Google, and other ad networks Google work with, will determine in real time the highest bidder for a particular keyword/search term.
If you win the bid, your ad will be placed.
This means that your daily budget is not set in stone, and your CPC (cost-per-click) is not constant.&lt;/p&gt;
&lt;p&gt;For some days you will pay more than your daily budget, sometimes up to 2x more, while on other days you won’t reach your budget.
Same goes for CPC.
Some clicks will be more expensive than others.&lt;/p&gt;
&lt;p&gt;What Google does guarantee is that you won’t pay more than your daily budget times 30 days, per each month.
So if your daily budget is €10, you won’t pay more than €300 per month.
But it’s not evenly spread among the days.
Some days you might get more traffic and pay more, while other days you get less traffic and pay less.
So just keep an eye on it.&lt;/p&gt;
&lt;p&gt;One more thing about daily budget.
I was experimenting with different daily budgets ranging from €20 a day, all the way down to €2.5 a day.
It’s important to note that every time you change your budget, Google does bid strategy adjustment.
It takes anywhere between 3 and 5 days, and during that period your bid strategy is not optimal.
So make sure to let it finish the strategy adjustment before jumping into conclusions.
This is indicated in the status of the campaign.&lt;/p&gt;
&lt;h2 id=&quot;1-out-of-10-out-of-100&quot;&gt;1 out of 10 out of 100&lt;/h2&gt;
&lt;p&gt;Now, lets talk about conversion.
My current click-through rate (CTR)—the amount of people who clicked on an ad as a percentage of total impressions—stands on 8.5%.
That’s roughly 8 click per every 100 impressions.&lt;/p&gt;
&lt;p&gt;My conversion rate is hard to tell, because I didn’t actively track conversions.
I can dissect the traffic and make assumptions which will lead me to about 5% conversion rate.
This means that out of 100 people who clicked the ad, only 5 made a purchase.&lt;/p&gt;
&lt;p&gt;If I round the numbers a bit, out of every 1000 people, I got around 100 clicks, out of which I got about 5 sales.
This means that my conversion rate from impression to purchase is 0.5%.
It’s a bit lower, as I’ve heard of 1% number, but it depends a lot on how optimized the campaign is.&lt;/p&gt;
&lt;h2 id=&quot;campaign-optimization&quot;&gt;Campaign optimization&lt;/h2&gt;
&lt;p&gt;Google has nice tips to help you optimize your campaign.
Some of them are useful, while others are just annoying (like the advertiser verification, which I never completed, and I doubt it affects the placement strategy).
I would put attention to things like: keywords, titles, and descriptions.&lt;/p&gt;
&lt;p&gt;I never had a perfect 100% campaign score, but I was around 82% most of the time.
Keep in mind that more keywords is not necessarily better.
Remember, your ad is not shown every time someone hit a keyword you are targeting, hence you might spend your successful bids for low performing keywords.
It’s better to focus on fewer keywords, but ones that have bigger potential to drive sales.&lt;/p&gt;
&lt;p&gt;Another tip is to pay attention to trademarks.
I’m not entirely sure how it works, but when I tried to target keywords like “fax with paypal”, I got a warning that some of my ads are limited due to trademarks in the ad description (in that case: PayPal).
So keep an eye on that, and make sure to remove keywords that might limit your ads.&lt;/p&gt;
&lt;p&gt;In addition to all that you can also set up ad schedule.
If you target the US, you might want to target US waking hours, instead of wasting your budget on a 3am guy who accidentally clicks your links while searching for “jet max”, but misspells it with “just fax”.
The example is made up, but you get the point.
Your goal should be to play with the parameters as you go, in order to squeeze the maximum from your daily budget.
Monitor your campaign, analyze the data, and iterate.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I was terrified to start with Google Ads.
When I first saw the recommended daily budget, it was around €300 per day, which means around €9,000 per months.
As a bootstrapped solopreneur, I don’t have that money to blow on ads.
But even if I did, I’m not sure whether it would be the best use for that money.
This brings me to two lessons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You can have a daily budget that is 10 or even 100 folds less than what Google recommends.
It will drive less traffic, and less sales.
So don’t afraid to start with less.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ads are not for everyone.
When you get into ads, you need to think about the LTV (lifetime value) you will get from your customers.
My SaaS is a one-time use: people come, send fax, and go.
They might return, they might not.
If I make $4 per fax, but it cost me €10 in ads to acquire that customer, I’m loosing money.
If, however, you operate on subscription model, or sell a very expensive product—your LTV might be way higher, and the cost of ads might be justified.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Oh, and the interface for Google Ads is terrible.
It’s so bad, that I honestly don’t understand how people use it.
And it’s not only Google Ads, I peeked at Meta Ads as well, and the interface is terrible as well.&lt;/p&gt;
&lt;p&gt;One last thing.
Google Ads is not the only place where you can advertise.
More platforms exist such as: Meta Ads (which includes Facebook and Instagram), Reddit Ads, and even in Google Ads you can target different products such as Search, YouTube, etc.
It’s important to understand where your target audience is.
I doubt that people who want to send a fax online, hang out on Instagram, but if you sell a physical product, or a digital “guru”-like course—Instagram might be more suitable for you.&lt;/p&gt;
&lt;p&gt;If you like this kind of content, I share my progress on a semiregular basis on &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;Twitter&lt;/a&gt; and &lt;a href=&quot;https://mstdn.social/@skwee357&quot;&gt;Mastodon&lt;/a&gt;, so consider following me there as well.&lt;/p&gt;</content:encoded></item><item><title>Using AsciiDoc to write my two books</title><link>https://kudmitry.com/articles/asciidoc-for-book-writing/</link><guid isPermaLink="true">https://kudmitry.com/articles/asciidoc-for-book-writing/</guid><pubDate>Tue, 19 Dec 2023 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the past period of six months when I roamed around Central America, among the &lt;a class=&quot;internal new&quot; href=&quot;/articles/6-lessons-6-months-6-projects/&quot;&gt;six projects&lt;/a&gt; that I launched—two where books that I wrote.
The first book is about technical writing: &lt;a class=&quot;internal new&quot; href=&quot;/books/technical-writing-for-software-engineers/&quot;&gt;Technical Writing for Software Engineer - A Handbook&lt;/a&gt;.
The second one, which I wrote together with my wife, is about getting into tech: &lt;a class=&quot;internal new&quot; href=&quot;/books/from-applicant-to-employee/&quot;&gt;From Applicant to Employee - Your Blueprint for Landing a Job In Tech&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you decide to write a book, you need to choose what tool to use.
Among the most used one are Word (could be Microsoft or Google Docs), and Scrivener—which is the go-to app for writing.
But I’m special, so at first I wanted to use Markdown.&lt;/p&gt;
&lt;h2 id=&quot;why-markdown-and-why-not&quot;&gt;Why Markdown, and why not?&lt;/h2&gt;
&lt;p&gt;Markdown is a fantastic format.
It supports all the needed text styles such as &lt;em&gt;italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt;, and for non-supported styles like &lt;ins&gt;underline&lt;/ins&gt;, you can always use HTML (in that case &lt;code&gt;&amp;#x3C;ins&gt;&lt;/code&gt; tag).
And as some of you might know, this very blog you are reading at the moment, is written in Markdown.
In short—I really like Markdown.
Moreover, I wanted to use the same tools (NeoVim) as I do with my blog, so Markdown was the first obvious choice.&lt;/p&gt;
&lt;p&gt;However, as it turned out, Markdown is awful for books, or any technical documentation longer than an average blog post.
When writing a book, you need to have support for different elements which Markdown does not support natively, and without hacking.
Examples could be: multipage documents, admonitions (those &lt;em&gt;tip&lt;/em&gt; or &lt;em&gt;warning&lt;/em&gt; boxes you see in many technical books), example blocks, etc.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/admonition-note.png&quot; width=&quot;600&quot; alt=&quot;Example of a &amp;#x27;NOTE&amp;#x27; admonition from my book&quot; class=&quot;object-fill m-auto&quot; loading=&quot;lazy&quot;&gt;
&lt;figcaption&gt;
Example of a &quot;NOTE&quot; admonition from my book &lt;a href=&quot;/book/technical-writing-for-software-engineers&quot;&gt;Technical Writing for Software Engineers&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I didn’t even mention things like: converting the Markdown files to PDF and EPUB, adding cover page, support for referencing other pages/chapters, etc.
Of course, most of these issues can be hacked around.
My blog has support for admonitions, but I use a custom remark plugin.
Referencing other pages can be done using wiki-link styles, but they are not supported by Markdown parsers, and you need to bring in third-party plugins to your Markdown parser of choice.&lt;/p&gt;
&lt;p&gt;And bundling a bunch of &lt;code&gt;.md&lt;/code&gt; files into a PDF, can be hacked together with tools like &lt;a href=&quot;https://pandoc.org/&quot;&gt;pandoc&lt;/a&gt;—which is a great piece of software if you need to convert various text formats from one another.
And in fact, Markdown + pandoc is what I first tried to do with my book.
But quickly I learned that it’s too much hassle, and decided to explore other alternatives.&lt;/p&gt;
&lt;h2 id=&quot;asciidoc&quot;&gt;AsciiDoc&lt;/h2&gt;
&lt;p&gt;Before settling on AsciiDoc, I read about many different formats.
Word and Scrivener were ruled out immediately as I didn’t want to learn new tools (in case of Scrivener), and preferred to stay in my environment of keyboard driver writing experience with NeoVim.
I looked at tools like DocBook, which was ruled out because it’s heavily XML infested.
And finally, I was set on AsciiDoc.&lt;/p&gt;
&lt;p&gt;AsciiDoc is very similar to Markdown, but way more powerful.
You have the standard elements of the page like headers (but using &lt;code&gt;=&lt;/code&gt; instead of &lt;code&gt;#&lt;/code&gt;); text styles (&lt;strong&gt;*bold*&lt;/strong&gt;, __italic__); URLs (&lt;code&gt;https://yieldcode.blog[yield code();]&lt;/code&gt;); etc.
But in addition to the standard formatting elements, you get more things out of the box like admonitions, example blocks, variables that you can reference, table of contents, etc.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/asciidoc-example-block.png&quot; width=&quot;600&quot; alt=&quot;Example of an &amp;#x27;Example&amp;#x27; block from my book&quot; class=&quot;object-fill m-auto&quot; loading=&quot;lazy&quot;&gt;
&lt;figcaption&gt;
Example of an &quot;Example&quot; block from my, and wife&apos;s book &lt;a href=&quot;/book/from-applicant-to-employee&quot;&gt;From Applicant to Employee&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The syntax of AsciiDoc is not that hard, especially if you know Markdown already.
But one of the greatest things of AsciiDoc, is the software that comes with it: &lt;a href=&quot;https://asciidoctor.org/&quot;&gt;asciidoctor&lt;/a&gt;.
Not only it has a great &lt;a href=&quot;https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/&quot;&gt;AsciiDoc syntax references&lt;/a&gt;, but it also acts as a publishing tool chain for converting AsciiDoc to PDF, HTML, DocBook, etc.&lt;/p&gt;
&lt;h3 id=&quot;using-asciidoctor&quot;&gt;Using asciidoctor&lt;/h3&gt;
&lt;p&gt;I want to give a quick overview of how to set up &lt;code&gt;asciidoctor&lt;/code&gt;, especially for PDF.
On macOS, &lt;code&gt;brew install asciidoctor&lt;/code&gt; will install the program itself, together with support for PDF.
In order to add support for EPUB, you will have to install additional ruby gem using &lt;code&gt;gem install asciidoctor-epub3&lt;/code&gt;.
Consult the manual for your OS to get started.&lt;/p&gt;
&lt;p&gt;Converting an AsciiDoc file to PDF, is a very simple task:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;asciidoctor&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; asciidoctor-pdf&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -b&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; pdf&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; out.pdf&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; in.adoc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will convert &lt;code&gt;in.adoc&lt;/code&gt; to &lt;code&gt;out.pdf&lt;/code&gt; using &lt;code&gt;pdf&lt;/code&gt; backend (&lt;code&gt;-b&lt;/code&gt;) via requiring &lt;code&gt;asciidoctor-pdf&lt;/code&gt; library (&lt;code&gt;-r&lt;/code&gt;).
Since AsciiDoc supports including other AsciiDoc files, you can have a one &lt;code&gt;main.adoc&lt;/code&gt; that contains all your book information and metadata, and includes all other &lt;code&gt;.adoc&lt;/code&gt; files which are chapters or appendixes.&lt;/p&gt;
&lt;p&gt;I could end here, but I want to cover some more features.
With EPUB you don’t have much room to play.
The way EPUB is displayed in terms of fonts and theme—depends a lot on the device where it’s being rendered.
PDF, however, is different, and you have more control over it.&lt;/p&gt;
&lt;p&gt;When converting AsciiDoc to PDF, you can use a theme that will define how your PDF will be rendered.
You control the theme with two CLI arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pdf-themesdir&lt;/code&gt;: accepts a directory where a bunch of &lt;code&gt;yml&lt;/code&gt; files named &lt;code&gt;theme-{}.yml&lt;/code&gt; will exist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pdf-theme&lt;/code&gt;: accepts a string, which is the name of the theme&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, if inside your &lt;code&gt;themesdir&lt;/code&gt; you have a file named &lt;code&gt;theme-book.yml&lt;/code&gt;, you can pass &lt;code&gt;pdf-theme=book&lt;/code&gt; to the tool in order to use your theme.&lt;/p&gt;
&lt;p&gt;There is a default theme that &lt;code&gt;asciidoctor&lt;/code&gt; will use if you do not specify one, but if you want to at least change the fonts, you will have to create your own theme.
Luckily, you don’t need to create an entire theme from scratch, you can just extend the default theme and overwrite the parts you want/need.
In addition to that, there are many themes you can find on the internet that you can use.
And in case you do want to create a custom theme, &lt;a href=&quot;https://docs.asciidoctor.org/pdf-converter/latest/theme/&quot;&gt;here is the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;tying-it-all-together-with-makefile&quot;&gt;Tying it all together with Makefile&lt;/h3&gt;
&lt;p&gt;The AsciiDoc for my two books lives inside a git repository.
From here, I build it to PDF and EPUB using &lt;code&gt;asciidoctor&lt;/code&gt; and upload to online stores.
I want to create a CI/CD pipeline, at some point, that will build the books and upload them automatically, but for now I don’t have many revisions, so I do it manually.&lt;/p&gt;
&lt;p&gt;However, I do automate the generation process with Makefile.
For each book, I have a &lt;code&gt;book.mk&lt;/code&gt; file that generates both EPUB and PDF files of the book.
Then, each &lt;code&gt;book.mk&lt;/code&gt; is included in the main &lt;code&gt;Makefile&lt;/code&gt; which I run every time I want to generate a book.&lt;/p&gt;
&lt;h2 id=&quot;additional-tools-i-use&quot;&gt;Additional tools I use&lt;/h2&gt;
&lt;p&gt;In addition to AsciiDoc and NeoVim, I use some other tools that helped me with writing my books.
Inside NeoVim I use &lt;code&gt;ltex&lt;/code&gt; which is language server that runs spell checking using LanguageTools.
I used macOS built it text-to-speech in order to do proofreading, in addition to proofreading myself multiple times.&lt;/p&gt;
&lt;p&gt;For complicated sentences, I used &lt;a href=&quot;https://hemingwayapp.com/&quot;&gt;Hemingway Editor&lt;/a&gt; in order to simplify them.
The thing with Hemingway though, is to not just follow its recommendation.
I believe that sometimes it’s fine to have complex sentences, or sentences in passive voice.
But it’s a good tool nevertheless to improve your writing.&lt;/p&gt;
&lt;p&gt;One last advice I want to give you.
It’s not mine, I saw it from multiple blogs.
Writing one sentence per line.
With formats like AsciiDoc and Markdown, you can write one sentence per line, and multiple line will be rendered as one paragraph.
You separate paragraphs with blank line.&lt;/p&gt;
&lt;p&gt;Writing this way makes it easier to edit specific sentence, or scrap entire sentences.
In NeoVim I would just &lt;code&gt;dd&lt;/code&gt; or &lt;code&gt;cc&lt;/code&gt; a line to delete/edit the sentence.
One sentence per line is also a good visual indicator for long sentences, that are candidates for simplification.
Lastly, &lt;code&gt;gitdiff&lt;/code&gt; is easier on the eyes with one sentence per line.
This is how it looks:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;asciidoc&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;Imagine you want to tell a story.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;You start with describing the setting.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;A forest for example.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;Inside this forest, Rust and Go are doing a picnic.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;And they eat C++.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it.
I hope that if you would like to write a book in the future, that this article will be helpful to you.&lt;/p&gt;</content:encoded></item><item><title>6 lessons from building 6 projects, while nomading for 6 months</title><link>https://kudmitry.com/articles/6-lessons-6-months-6-projects/</link><guid isPermaLink="true">https://kudmitry.com/articles/6-lessons-6-months-6-projects/</guid><pubDate>Sun, 10 Dec 2023 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Six months ago, I boarded a one way flight to Costa Rica.
A month prior to that, I quit my job after being &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;burnt out&lt;/a&gt;.
My apartment lease ended, I sold almost everything I own, and packed the rest into a backpack and a suitcase.&lt;/p&gt;
&lt;p&gt;I had a simple plan: slow travel, work on my mental and physical health, and &lt;strong&gt;build a sustainable online business&lt;/strong&gt;.
I didn’t have a job, and was relying solely on my savings.
This gave me all the time I need to focus on the above goals.&lt;/p&gt;
&lt;p&gt;With time, from Costa Rica I moved to Guatemala, and then to Mexico.
And after exactly 6 months, at the beginning of December, I boarded my last flight in 2023 for a permanent move to The Netherlands, a country to where my wife and I—relocated.
I want to give you a short overview of my projects, and what I learned from them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: I actually launched 8 different products.&lt;/em&gt;
&lt;em&gt;However, 4 of them can be put in 2 categories, so in total I launched 6 different product categories, each with different mindset.&lt;/em&gt;
&lt;em&gt;Felt like I need to clarify this, in order to avoid comment on the somewhat, misleading title.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;lesson-1-build-it-and-they-will-comeis-a-lie&quot;&gt;Lesson #1: Build it, and they will come—is a lie&lt;/h2&gt;
&lt;p&gt;After landing in Costa Rica, and taking the first few days to adjust to the time difference, eventually I got to work on my first project: &lt;a href=&quot;https://silentflip.com&quot;&gt;SilentFlip&lt;/a&gt;.
I had the idea for SilentFlip in my head long before I decided to quit my job and nomad.
The rationale was simple: take an existing product and build a copy that targets a different market.
In my case, I wanted to build a remote feature toggling service that will target solopreneurs and small startups.
Existing solutions are targeting big enterprises that can afford to pay per seat, which many solo builders can’t.&lt;/p&gt;
&lt;p&gt;After spending some time building the MVP, and doing the landing page, I finally bought the domain, and shared the launch post on LinkedIn (where I’ve been somewhat active for the past 1.5 years).
I got a modest amount of views to the post, and some small amount of likes.
The landing page had no option to purchase the product, and instead I was collecting emails based on the desired price tier (I had 3 tiers).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/linkedin_silentflip.png&quot; width=&quot;600&quot; alt=&quot;LinkedIn Post about SilentFlip&quot; class=&quot;object-fill m-auto&quot; loading=&quot;lazy&quot;&gt;
&lt;figcaption&gt;
&lt;a target=&quot;_blank&quot; href=&quot;https://www.linkedin.com/posts/kudmitry_affordable-feature-management-and-toggling-activity-7077694763998621696-rnFn?utm_source=share&amp;#x26;utm_medium=member_desktop&quot;&gt;My Post about SilentFlip on LinkedIn&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I got this tip from Tim Ferriss’s book—The 4-hour work week—where he recommended setting up a landing page where you would collect customer intentions to buy the product.
The idea behind this is that you don’t want to spend time developing a product if you can’t really sell/market it.
However, looking back at it, I believe this is a wrong approach.
I’ll cover this in a different lesson below.&lt;/p&gt;
&lt;p&gt;But back to the original lesson.
I, like many technical founders, believed at that time in the “&lt;em&gt;build it, and they will come&lt;/em&gt;” mantra.
I learned the hard way that this is not how it works.
Unless you build a unicorn, there is a big chance that every market you will try—is saturated with competition.
And when people search for a remote feature toggling service, there is a high chance they won’t land on my landing page.&lt;/p&gt;
&lt;p&gt;In the end, nobody cares how perfect your product from &lt;em&gt;a technical viewpoint&lt;/em&gt;, and this is what many technical solopreneurs/indie-hackers don’t get.
You can build a product in a saturated market, and get customers, as long as you don’t just throw it in the void an expect people to magically find it because “&lt;em&gt;it’s written using amazing technology!&lt;/em&gt;”&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/buildit_mastodon.png&quot; width=&quot;600&quot; alt=&quot;Mastodon post about build it and they will come&quot; class=&quot;object-fill m-auto&quot; loading=&quot;lazy&quot;&gt;
&lt;figcaption&gt;
My &lt;a href=&quot;https://mstdn.social/@skwee357/111154509290996801&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/skwee357/status/1708122800630775925?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1708122800630775925%7Ctwgr%5E5a4b15329418570ad20946fff865d557da01d21b%7Ctwcon%5Es1_c10&amp;#x26;ref_url=https%3A%2F%2Fpublish.twitter.com%2F%3FhideConversation%3Donquery%3Dhttps3A2F2Ftwitter.com2Fskwee3572Fstatus2F1708122800630775925theme%3Ddarkwidget%3DTweet&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt; post about &quot;Build it, and they will come&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;lesson-2-dont-build-communities&quot;&gt;Lesson #2: Don’t build communities&lt;/h2&gt;
&lt;p&gt;Like a good indie-hacker, after seeing no traction from my first product, I ditched it and launched not one, &lt;strong&gt;but two&lt;/strong&gt;, different products: Artisan Brew Coffee and PetAway.&lt;/p&gt;
&lt;p&gt;Artisan Brew Coffee is a community for coffee lovers.
Not the Nespresso type, but rather the pour over type.
I got this idea after visiting a coffee plantation in Costa Rica, and as a coffee lover, decided to launch this community.&lt;/p&gt;
&lt;p&gt;PetAway is a “scratch your own itch” type of product.
Since I didn’t plan to come back to my home country, but rather wanted to end up in The Netherlands—my wife and I had to take our dog with us.
And if you ever travelled with pets—you know what hell you need to go through.
So I launched this community together with my wife in order to share knowledge for travelling with pets.&lt;/p&gt;
&lt;p&gt;And here comes the second lesson: &lt;strong&gt;don’t build communities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When you build a product that does not need a community, your success depends on two things: the quality of the product, and how widespread it is (read: marketing).
It doesn’t matter how many people bought this product yesterday.
As long as you are able to attract new customers, you will make profit.&lt;/p&gt;
&lt;p&gt;With the community, on the other hand, you depend a lot on the members of the community.
Nobody wants to be a member of a community which doesn’t have any members.
So you are stuck in a catch-22 scenario: you need members for your community, but in order to gain more members, you already need to have members.
There are ways to overcome this such as creating lots of content, or even fake profiles, in order to attract initial members.
But it’s just another thing you need to take care of, in addition to moderating the community.
And as a solopreneur—you already have a lot of things you need to take care of.&lt;/p&gt;
&lt;h2 id=&quot;lesson-3-make-sure-to-have-a-business-plan&quot;&gt;Lesson #3: Make sure to have a business plan&lt;/h2&gt;
&lt;p&gt;My two next products were LibrePress.io—an “own your content” type of newsletter distribution platform, I even wrote a &lt;a class=&quot;internal new&quot; href=&quot;/articles/own-your-content/&quot;&gt;blog post&lt;/a&gt; about it; and &lt;a href=&quot;https://remoteornot.fyi&quot;&gt;Remote or Not&lt;/a&gt;—A list of all companies by their remote and work-from-home policy.
Both of them “failed” because I didn’t have a solid business plan.&lt;/p&gt;
&lt;p&gt;LibrePress was born out of idealistic motivation: I’m a big believer in owning your content, and I wanted a newsletter platform that focuses on distribution without caring how you create the content.
The idea was to distribute RSS feeds, or upload markdown files.
I wanted to switch my &lt;a href=&quot;https://yieldnews.substack.com&quot;&gt;newsletter&lt;/a&gt; to it, soon after launch.
However, what I failed to take into account, is the running cost.
Given the amount of newsletter readers I have, and the pricing for the hosting+email provider—I would end up in a range of $15-$20 per month, just to distribute my newsletter.
This is too expensive for me at the moment, given the fact that currently I pay nothing (in monetary terms), and other services exist that charge less.
Obviously, if I had paying customers, that would make it more cost-effective to switch, but I didn’t even come up with a pricing plan for LibrePress.&lt;/p&gt;
&lt;p&gt;Remote or Not, on the other hand, was born out of my endless side-project ideas list.
At the time of building it I was searching for a job with a strong preference for hybrid or fully remote policy, and couldn’t find a reliable source of such information.
While I still maintain Remote or Not, it failed on two fronts: it’s a somewhat community based product (I depend heavily on contributions); and it didn’t have any clear business plan.
It doesn’t have enough traffic to put ads.
I didn’t want to charge job-seekers in order to get access to the information.
As for companies, I’ve added a form for a company representative to reach out to me and provide official remote work policy for their company, with the intention to charge companies in the future.
So far, no company reached out.&lt;/p&gt;
&lt;p&gt;And, speaking about pricing plans…&lt;/p&gt;
&lt;h2 id=&quot;lesson-4-if-you-dont-charge-you-dont-make-money&quot;&gt;Lesson #4: If you don’t charge, you don’t make money&lt;/h2&gt;
&lt;p&gt;Sounds obvious, right?
But so many indie-hackers, including myself, ignore this lesson.
There is this mental barrier to charge money for something.
The moment you reach this milestone, you start to question yourself: am I good enough; is the product worth it; what if nobody buys it; etc.
But the truth is, and circling back the advice from Tim Ferriss’s book, if you don’t charge—you don’t make money.&lt;/p&gt;
&lt;p&gt;Collecting emails is not the same as collecting money.
People are way more willing to sign up to alpha, beta, closed releases, and any other free services.
But the moment they need to pay for something, it’s a completely different mindset.
Tim’s advice was focusing more on physical products, where you have a high cost of manufacturing.
In that case yes, I think it’s better to set up a fake shop, but guide the user through purchase flow, to clearly identify people who are &lt;strong&gt;willing to pay now&lt;/strong&gt;, rather than collect emails of people who &lt;em&gt;might pay in the future&lt;/em&gt;.
But when you build a digital product, the cost of which is nearly zero, you need to charge money from the get-go.
Otherwise, you are building a pet project, and not a business.&lt;/p&gt;
&lt;p&gt;And it took me almost 4 months to realize it, and eventually launch &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;—a super simple, e-fax solution.
It was the first product I finished from A to Z.
It had a working MVP, and I was charging money from the start.
So far, JustFax and the two other products I’ll talk about in the next lesson, are the only products that make me money.&lt;/p&gt;
&lt;p&gt;And coming back to the previous lesson, JustFax online was the first product where I actually had a business plan.
I created an Excel spreadsheet and calculated different cost scenarios of operating such service: how much would I pay for sending the faxes, how much is the infrastructure, how much should I charge, etc.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/justfax_price_twitter.png&quot; width=&quot;600&quot; alt=&quot;Calculating the price for JustFax&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;My &lt;a href=&quot;https://x.com/skwee357/status/1712979151085277511?s=20&quot; target=&quot;_blank&quot;&gt;Twitter post&lt;/a&gt;, also available on &lt;a href=&quot;https://mstdn.social/@skwee357/111230388810368336&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;, about calculating the price for &lt;a href=&quot;https://justfaxonline.com&quot; target=&quot;_blank&quot;&gt;JustFax Online&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;lesson-5-think-about-distribution&quot;&gt;Lesson #5: Think about distribution&lt;/h2&gt;
&lt;p&gt;Looking back at SilentFlip, Artisan Brew Coffee, PetAway, LibrePress, and Remote or Not, the reason they failed—is lack of distribution channels.
Distribution channels can be advertisement, but it also can be a personal brand.
And this is what happened to me when I launched my two books: &lt;a href=&quot;/book/technical-writing-for-software-engineers&quot;&gt;Technical Writing for Software Engineers - A Handbook&lt;/a&gt; and &lt;a href=&quot;/book/from-applicant-to-employee&quot;&gt;From Applicant to Employee - Your Blueprint for Landing a Job in Tech&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Both of my books are available on Gumroad, which is a marketplace for digital goods.
And while marketplaces and app-stores are a great way to distribute your products, they are, like anything else, saturated with content.
There is a way to succeed on marketplaces/app-stores, but this is a topic for another blog post.
Most of the sales of my books came from this blog, my newsletter, and my social network presence (Twitter, Mastodon, and LinkedIn).&lt;/p&gt;
&lt;p&gt;I started to write my first book after the “failure” of Artisan Brew Coffee and PetAway, and at that time I started to be active on Twitter, Mastodon, and LinkedIn (where I’ve been active, but mainly promoting my blog).
Being active on social media is a good distribution channel.
If you are consistent, share quality content, and try to engage with the community—you essentially get “free” customers.
The &lt;em&gt;#buildinpublic&lt;/em&gt; hashtag is filled with solopreneurs and indie-hackers that are building products and sharing their experience.&lt;/p&gt;
&lt;p&gt;But the problem is—you need to know your audience.
If you are branding yourself as a software engineer, like I do, it becomes easier to distribute products targeting software engineers.
And I saw it with both of my books.
As I’ve said, most of my sales came through Twitter/Mastodon and my blog.
However, if we look at JustFax—the target audience is not software engineers or indie-hackers.
And while I got great feedback from both Twitter and Mastodon about JustFax, most of the sales didn’t come from these platforms.&lt;/p&gt;
&lt;p&gt;And this is why it’s important to think about your distribution channels.
Moreover, what worked for one product, will not necessarily work for another.
This is why with JustFax, I’m focusing more on SEO and organic growth, and considering experimenting with advertisement.&lt;/p&gt;
&lt;h2 id=&quot;lesson-6-you-only-fail-when-you-quit-or-in-other-words-consistency-wins&quot;&gt;Lesson #6: You only fail when you quit, or in other words: consistency wins&lt;/h2&gt;
&lt;p&gt;Few days ago I had a chat with my friend.
He is also an indie-hacker who is trying to build a side-business.
We talk once in a while and share ideas, and revelations.
During our call, we were talking about consistency, and I came up with a great analogy.&lt;/p&gt;
&lt;p&gt;Imagine you play a game.
I grew up playing Dungeon Siege, so I’ll describe the setup from it.
You are spawned as a peasant in burned down village.
The entire kingdom is infested with evil creatures such as orcs, and the only hope to save the kingdom from evil force—is you.
In this village, behind a burning house, you find a pitchfork.
You take it and go kill your first two orcs.
And get your first level-up to level two where you can assign skill points.&lt;/p&gt;
&lt;p&gt;But then you quit the game, and the next time you relaunch it, you don’t start where you’ve quit—you start from the beginning, with the pitchfork behind a burned house, on first level, with both orcs alive, and zero skill points to assign.&lt;/p&gt;
&lt;p&gt;This is how it works if you are not consistent.
Every time you quit, or start a new project—you start from &lt;strong&gt;zero&lt;/strong&gt;.
Yes, you prefect some craft.
I, for example, can build landing pages in less than a day now, because I built 6 of them.
But only with JustFax I was able to get to a new level, unlock new abilities and knowledge.
None of my other products reached a point where I had to take care of customers, collect payments, deal with bug fixes, focus on ads and SEO, etc.&lt;/p&gt;
&lt;p&gt;If I had to start over again today, I would focus on one product for as long as possible, taking into account all the above lessons about community, distribution, and charging from the start.
The moment you switch to a new product, and indie-hacker (myself included) like to switch often, you don’t start from the same point you left your last project—you start from &lt;strong&gt;zero&lt;/strong&gt;.
Yes you have prior experience, and starting over will be faster.
But if none of your products reached a phase where you need to focus on SEO, you can start 100500 new products—but you won’t learn about SEO.&lt;/p&gt;
&lt;p&gt;This is why it’s important to stay consistent.
It’s perfectly fine to quit, and start a new product.
But it’s only when you go through the whole journey, starting from idea, to product, to launch, to marketing and distribution, to taking payments, to fixing bugs and doing customer support; only then you are allowed to quit and start a new product.
Otherwise, you risk running the same 100 meters of a marathon over and over again.
Which is fine, if you want to be the fastest runner of 100 meters.
But if you want to finish the marathon, you need to stay consistent.&lt;/p&gt;
&lt;h2 id=&quot;bonus-lesson-7-share-it-with-the-world&quot;&gt;Bonus lesson #7: Share it with the world&lt;/h2&gt;
&lt;p&gt;I remember how scared I was before launching SilentFlip on LinkedIn.
You get all these thoughts: you are not good enough; nobody needs it; you will just embarrass yourself.
And I had the same feeling with every other product I launched afterward.
It goes away little-by-little, but if you don’t share your work out there, you won’t succeed.
No matter how stupid you feel, just share it.&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final Words&lt;/h2&gt;
&lt;p&gt;These were amazing 6 months.
I’ve acquired an enormous amount of knowledge, and most importantly &lt;strong&gt;practical&lt;/strong&gt; knowledge, which worth more than every theoretical knowledge you get.
In addition to that, this has been a great time to cure my burnout and restart my career.
I’m still analyzing everything from this trip, and planning to compile it to a few more blog posts, so stay tuned.&lt;/p&gt;
&lt;p&gt;I would like to use the final words as an opportunity for self-promotion.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you ever need to send a fax online, consider giving &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax online&lt;/a&gt; a try.
I’m open to feedback, so feel free to reach out to me.&lt;/li&gt;
&lt;li&gt;If you are a &lt;strong&gt;software engineer&lt;/strong&gt; who want to take your career to the next level, you need to be able to write technical documents.
In that case, consider getting my handbook: &lt;a href=&quot;/book/technical-writing-for-software-engineers&quot;&gt;Technical Writing for Software Engineers&lt;/a&gt;.
It contains all my knowledge regarding technical writing.&lt;/li&gt;
&lt;li&gt;If you found yourself laid off from your job; you want to switch to a new job; or you want to get into tech—consider getting my and my wife’s book: &lt;a href=&quot;/book/from-applicant-to-employee&quot;&gt;From Applicant to Employee: Your Blueprint for Landing a Job in Tech&lt;/a&gt;.
It contains my and my wife’s (Senior Talent Acquisition, Technical/Executive Recruiter, and Career Coach) entire knowledge about technical interviews, how to pass them, and how to prepare.
We used tips from this book in our recent search for a new job.&lt;/li&gt;
&lt;li&gt;If you are interested in starting a side business, but don’t have ideas or ability to build your own product—I offer SilentFlip and Artisan Brew Coffee for sale.
You can buy any part you want: domain, website, potential leads.
I’m open to sell LibrePress.io and &lt;a href=&quot;https://remoteornot.fyi&quot;&gt;Remote or Not&lt;/a&gt; as well, as I don’t have much time to work on them.
Reach out to me (email, or social networks), and let’s discuss it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And as always, consider subscribing to my &lt;a href=&quot;https://yieldnews.substack.com&quot;&gt;newsletter&lt;/a&gt; to never miss an update from this blog.
Follow me on &lt;a href=&quot;https://twitter.com/@skwee357&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://mstdn.social/@skwee357&quot;&gt;Mastodon&lt;/a&gt;, and &lt;a href=&quot;https://linkedin.com/in/kudmitry&quot;&gt;LinkedIn&lt;/a&gt;—where I share a lot about software engineering, as well as solopreneurship, indie-hacking, and building in public my projects.&lt;/p&gt;</content:encoded></item><item><title>Web app localization. In Rust.</title><link>https://kudmitry.com/articles/webapp-localization-in-rust/</link><guid isPermaLink="true">https://kudmitry.com/articles/webapp-localization-in-rust/</guid><pubDate>Thu, 30 Nov 2023 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Localization, often times stylized as &lt;code&gt;l10n&lt;/code&gt;, is a process of adapting your website or web application to meet the requirements of specific target market (known as “locale”).
It’s one of the 3 processes of going global, which include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Translation (&lt;code&gt;t9n&lt;/code&gt;)—the process of translating the content to a target language&lt;/li&gt;
&lt;li&gt;Localization (&lt;code&gt;i10n&lt;/code&gt;)—the process that I described above&lt;/li&gt;
&lt;li&gt;Internationalization (&lt;code&gt;i18n&lt;/code&gt;)-the process of adapting numbers, prices, dates, etc—to a target market/locale&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Today, I want to talk about localization, why I think some websites do it wrong, and how I did it in my recent side project: &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
Are you ready?
Bon voyage!&lt;/p&gt;
&lt;p&gt;Oh, Rust code included 🦀!&lt;/p&gt;
&lt;h2 id=&quot;step-oneunderstanding-your-users&quot;&gt;Step one—understanding your users&lt;/h2&gt;
&lt;p&gt;I want to start with the obvious—how do you understand where your users are coming from?&lt;/p&gt;
&lt;h3 id=&quot;what-not-to-do&quot;&gt;What not to do&lt;/h3&gt;
&lt;p&gt;Many websites, Google and PayPal among them, are doing it wrong.
For some reason, they are obsessed with GeoIP.
But let me tell you a story.&lt;/p&gt;
&lt;p&gt;For the past 6 months, I’m nomading in Central America.
People in Central America—speak Spanish.
I, however, &lt;em&gt;hablo poco español&lt;/em&gt; (Spanish for “I don’t speak too much Spanish”).
But both Google and PayPal (and I believe there are many other websites), insists on serving me their websites in Spanish.&lt;/p&gt;
&lt;p&gt;With PayPal, it’s easy, they have a language switcher at the bottom, so I waste 2 seconds of my life every time I need to pay with PayPal.
Google, on the other hand, is more complicated.
The other day, I was given access to a Google Document through my non Google account, and after opening it, I saw that it was entirely on Spanish.
I wasn’t able to find a way to revert it back to English.
Luckily I know the Google Docs interface well enough that I was able to manage it.&lt;/p&gt;
&lt;p&gt;The reason I got served the Spanish version of their websites—is because my IP is originated from a Spanish-speaking country, in my case Mexico.
Switching on VPN to a different country, with different language, would serve their website in that particular language.&lt;/p&gt;
&lt;p&gt;I understand the desire of whoever made this decision to have a good user experience, but in my opinion it’s horrible, and there is a better way to do it.&lt;/p&gt;
&lt;h3 id=&quot;meet-the-accept-language-header&quot;&gt;Meet the Accept-Language header&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Accept-Language&lt;/code&gt; is a standard HTTP header that every browser sends.
Instead of relying on GeoIP, the browser tells the server what languages I prefer, based on the settings of my device.
My macOS is in English, therefor my &lt;code&gt;Accept-Language&lt;/code&gt; looks like this: &lt;code&gt;Accept-Language: en-US,en;q=0.5&lt;/code&gt;.
Yes, &lt;code&gt;Accept-Language&lt;/code&gt; can even have multiple languages.
Every OS has a setting to set the preferred languages, and countries, and desktop applications seem to respect that.
But web being web, prefers to use fancy GeoIP methods to wrongly guess my language.&lt;/p&gt;
&lt;p&gt;But enough talking, let’s see how we can extract &lt;code&gt;Accept-Language&lt;/code&gt; header with Rust and &lt;code&gt;axum&lt;/code&gt; framework.
If you are interested to read my take on Rust and &lt;code&gt;axum&lt;/code&gt; for web development, make sure to check my last blog post &lt;a class=&quot;internal new&quot; href=&quot;/articles/building-a-webapp-in-rust/&quot;&gt;Building a web app in Rust&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I always prefer to use &lt;code&gt;TypedHeader&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;While writing this article, I found out that &lt;code&gt;axum&lt;/code&gt; 0.7 was released.
And it brings a breaking change to the &lt;code&gt;header&lt;/code&gt; module.
&lt;code&gt;TypedHeader&lt;/code&gt; is no longer part of &lt;code&gt;axum&lt;/code&gt;, and was moved to &lt;code&gt;axum-extra&lt;/code&gt;.
You can read more about it in the &lt;a href=&quot;https://github.com/tokio-rs/axum/blob/main/axum/CHANGELOG.md&quot;&gt;changelog&lt;/a&gt;.
I haven’t upgraded to &lt;code&gt;axum 0.7&lt;/code&gt; yet, therefor the rest of this article relies on &lt;code&gt;axum 0.6.x&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;TypedHeader&lt;/code&gt; allows us to extract request headers in a type-safe way.
&lt;code&gt;axum&lt;/code&gt; has many headers defined that you can use with &lt;code&gt;TypedHeader&lt;/code&gt;, but unfortunately, &lt;code&gt;Accept-Language&lt;/code&gt; is not one of them.
So I had to write my own.
It’s not that hard.&lt;/p&gt;
&lt;h4 id=&quot;defining-the-struct-for-accepted-language&quot;&gt;Defining the struct for accepted language&lt;/h4&gt;
&lt;p&gt;First, we need to write a struct that will hold a pair of language identifier and quality values, essentially holding a string like this: &lt;code&gt;en-US;q=0.5&lt;/code&gt;.
Let me dump the code first, and then we will go over it.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;#[derive(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Debug&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; enum&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptLanguageError&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    #[error(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Invalid value&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    InvalidValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    pub&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    pub&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;f32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Eq&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; PartialEq&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; eq&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.value.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;eq&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.value)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; PartialOrd&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; partial_cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Option&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ordering&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Some&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Ord&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ordering&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ordering&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Greater&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; other&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ordering&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Less&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cmp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Ordering&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Equal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a very simple struct that holds a string and a float, and supports comparison and ordering (based on quality value).&lt;/p&gt;
&lt;p&gt;Next, we need a way to create such structs.
Since the most common scenario is creating them from a string, I’ve opted for implementing &lt;code&gt;FromStr&lt;/code&gt; trait.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; FromStr&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Err&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptLanguageError&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; from_str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; s&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Some&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptLanguageError&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;InvalidValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;is_empty&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptLanguageError&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;InvalidValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; quality&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Some&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;and_then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;q&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; q&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;strip_prefix&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;q=&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;f32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap_or&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;            1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to_string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            quality&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, again, we don’t have anything special.
We take a string such as &lt;code&gt;en;q=0.5&lt;/code&gt;, and break it to its value and quality components.
Keep in mind that quality is optional, and default to &lt;code&gt;1&lt;/code&gt; if not specified.&lt;/p&gt;
&lt;p&gt;Lastly, in order for &lt;code&gt;TypedHeader&lt;/code&gt; to extract our custom header, we have to implement the &lt;code&gt;Header&lt;/code&gt; trait.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Vec&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Header&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AcceptLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() -&gt; &amp;#x26;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;HeaderName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;ACCEPT_LANGUAGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; decode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;values&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    where&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Sized&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        I&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Iterator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Item&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x26;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;HeaderValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; values&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;ok_or_else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;invalid&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)?;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; str&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to_str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Accept-Language must be a string&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; languages&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;from_str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;collect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vec&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptLanguageError&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map_err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;invalid&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;())?;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        languages&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;sort&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;AcceptLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;languages&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; encode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;axum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;HeaderValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;values&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; E&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; val&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; self&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;iter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; format!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;{};q={}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.value, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.quality))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; val&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; HeaderValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from_str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Accept-Language must be valid&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        values&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;std&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;iter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;once&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I said, &lt;code&gt;Accept-Header&lt;/code&gt; is an array of language identifiers and quality values, separated by comma.
Therefor, I break the string by the comma character, and map it to my custom &lt;code&gt;AcceptedLanguage&lt;/code&gt; struct.&lt;/p&gt;
&lt;h3 id=&quot;extracting-preferred-language&quot;&gt;Extracting preferred language&lt;/h3&gt;
&lt;p&gt;Next step, is to create a middleware that will extract the preferred language of the user.
The purpose of this middleware, is to look at the &lt;code&gt;Accept-Language&lt;/code&gt; header, and compare the values to our supported languages.
JustFax is offered in &lt;a href=&quot;https://justfaxonline.com/en&quot;&gt;English&lt;/a&gt;, &lt;a href=&quot;https://justfaxonline.com/de&quot;&gt;German&lt;/a&gt;, and &lt;a href=&quot;https://justfaxonline.com/fr&quot;&gt;French&lt;/a&gt;, and their all defined in a &lt;code&gt;toml&lt;/code&gt; config:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supported_languages&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;de&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fr&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;You can have a better user experience if you decide to target different countries, differently.
For example American English (&lt;code&gt;en-US&lt;/code&gt;), is different from British English (&lt;code&gt;en-GB&lt;/code&gt;).
I didn’t go that deep, and opted to offering American English to all my English-speaking audience.
Apologies for that.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The middleware is not that hard:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;#[derive(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Clone&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; PreferredLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Option&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LanguageIdentifier&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; extract_preferred_language&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; IntoResponse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get_config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; span&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; tracing&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;span!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Level&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;TRACE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;preferred language extraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; _enter&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;enter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;into_parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; preferred_lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Option&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LanguageIdentifier&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;TypedHeader&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;accept&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extract&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TypedHeader&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AcceptLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            accept&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;iter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;filter_map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.value.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LanguageIdentifier&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.locale.supported_languages.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;contains&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;collect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vec&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LanguageIdentifier&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;first&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to_owned&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;            None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    tracing&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;event!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Level&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;TRACE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        &quot;extracted preferred language: {:?}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        preferred_lang&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; request&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from_parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;parts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extensions_mut&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;PreferredLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;preferred_lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I use another library, called &lt;a href=&quot;https://docs.rs/unic-langid/latest/unic_langid/&quot;&gt;&lt;code&gt;unic_langid&lt;/code&gt;&lt;/a&gt; that is able to parse strings such as &lt;code&gt;en-US&lt;/code&gt; into their respective language, region, script and variants.
It makes it easier to validate the locale strings, as well as comparing them.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The middleware takes the &lt;code&gt;Accept-Language&lt;/code&gt; that we’ve created earlier, map each language to &lt;code&gt;LanguageIdentifier&lt;/code&gt;, and filter out the ones that are not supported.
If you look again at the &lt;code&gt;Accept-Language&lt;/code&gt; header that we’ve implemented, it spits out a &lt;strong&gt;sorted&lt;/strong&gt; array of languages based on quality.
Therefore, we are guaranteed to pick the most preferred language that both the user, and our website, support.&lt;/p&gt;
&lt;p&gt;Once we identified the preferred language, we put it in a &lt;code&gt;axum&lt;/code&gt; extension, for future handlers to extract.
Keep in mind that it can be &lt;code&gt;None&lt;/code&gt; for cases where none of the languages in the &lt;code&gt;Accept-Langauge&lt;/code&gt; header are supported by us (or the header is empty/invalid).
In that case, I default to English as this is the main language of my application.&lt;/p&gt;
&lt;h2 id=&quot;step-twoserving-localized-pages&quot;&gt;Step two—serving localized pages&lt;/h2&gt;
&lt;p&gt;We now have a way to extract user’s preferred language, using cutting-edge technology &lt;del&gt;GeoIP&lt;/del&gt; &lt;code&gt;Accept-Language&lt;/code&gt; header.
The next step, is to serve the content in each language.&lt;/p&gt;
&lt;h3 id=&quot;defining-url-scheme&quot;&gt;Defining URL scheme&lt;/h3&gt;
&lt;p&gt;There are 4 main ways to define your multilingual URLs. Using a country specific domain such as &lt;code&gt;example.de&lt;/code&gt;; using a subdomain such as &lt;code&gt;de.example.com&lt;/code&gt;; using a subdirectory such as &lt;code&gt;example.com/de&lt;/code&gt;; and lastly, using query parameter such as &lt;code&gt;example.com?lang=de&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Domains are expensive, so the first option goes to trash.
Subdomains could be a good option, but I didn’t want to mess with DNS settings.
Query parameters are bad option, and are &lt;a href=&quot;https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites#locale-specific-urls&quot;&gt;not recommended by Google&lt;/a&gt;.
I opted for using subdirectories.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;It’s funny, now I see it.
On the above webpage by Google, they have a strong red warning that says: &lt;em&gt;Don’t use IP analysis to adapt your content.&lt;/em&gt;
But that’s what they are doing.
Side effects of being a big-corp.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After deciding on the URL scheme, you need to decide what do you do with the root URL.
Some websites leave the root URL to be the default language (for example, English).
Others, might show a language chooser on the root URL (I don’t like that approach).
I opted for a different approach: redirecting the root URL to a language specific URL.&lt;/p&gt;
&lt;p&gt;So when a user navigates to &lt;code&gt;justfaxonline.com/availability&lt;/code&gt;, he/she will be redirected (we will cover the redirection process in a bit), to a language specific subdomain, for example: &lt;code&gt;justfaxonline.com/de/availability&lt;/code&gt;.
In order to achieve this, I had to do some dancing with &lt;code&gt;axum&lt;/code&gt; router.&lt;/p&gt;
&lt;p&gt;I first needed to create the root routes:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; make_root_router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AppContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;redirect_handler&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/availability&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;redirect_handler&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;some&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; more&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; routes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;layer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from_fn&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;extract_preferred_language&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This router covers all the &lt;code&gt;GET&lt;/code&gt; pages, and instead of serving the HTML, it calls a &lt;code&gt;redirect_handler&lt;/code&gt; (hang on, we will get to it), which redirects to the appropriate locale subdomain.
Then, I had to create the actual router:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; main_router&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; web&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;merge&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;layer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from_fn&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;extract_preferred_language&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.locale.supported_languages {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    router&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;nest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;format!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/{}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to_string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to_lowercase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        main_router&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;layer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Extension&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;DetectedLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()))),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This router simply loops through all the supported languages from the config file, and creates a subdirectory for them.
In addition to that, the main router also gets the preferred user language, as well as the detected language (which is the actual language of this particular route).
We need them both in order to properly redirect the user, and provide a nice UX.&lt;/p&gt;
&lt;p&gt;Lastly, we need to talk about user experience.
First time users should be redirected based on their &lt;code&gt;Accept-Langauge&lt;/code&gt; header.
However, what if my computer is in Spanish, but when visiting JustFax Online, I prefer to use English?
Meet: the cookie.&lt;/p&gt;
&lt;p&gt;After the raise of SPAs (single page applications), it seems like people forgot about the existence of cookies.
But cookies are very handy to store non-sensitive user information.
They are being sent with each request, event if the request is made with Ajax.
You don’t need any specific JavaScript code to send them, unlike &lt;code&gt;LocalStorage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On my website, I have a language picker at the bottom that allows you to switch language.
Once you click it, there is some JavaScript on the client side that is used to set/overwrite the cookie, and then refresh the webpage.
On the backend, I read this cookie and do the proper redirection.
I’ll spare you the JavaScript code, but here is the backend code for &lt;code&gt;redirect_handler&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; redirect_handler&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    Extension&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;PreferredLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;preferred_langauge&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Extension&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PreferredLanguage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    OriginalUri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;uri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;OriginalUri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    jar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CookieJar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;impl&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; IntoResponse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get_config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; selected_language&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; jar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;lang&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        None&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;None&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        Some&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cookie&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) =&gt; &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cookie&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LanguageIdentifier&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Some&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap_or&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;None&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; selected_language&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;as_ref&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap_or_else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        preferred_langauge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;as_ref&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap_or&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.locale.default_language)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .locale&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .supported_languages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;iter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; item&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;matches&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        lang&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.locale.default_language;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get_url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;uri&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;lang&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    event!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Level&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;TRACE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;redirecting: {}&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    Redirect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;temporary&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a simple code that reads from the &lt;code&gt;CookieJar&lt;/code&gt;; if no cookie with the language exists, it reads the language from the preferred language (which comes from &lt;code&gt;Accept-Language&lt;/code&gt; header); and if it doesn’t exist, or none of them are supported, it falls back to the default language.&lt;/p&gt;
&lt;p&gt;Here you can also see the usage of &lt;a href=&quot;https://docs.rs/unic-langid/latest/unic_langid/struct.LanguageIdentifier.html#method.matches&quot;&gt;&lt;code&gt;unic_langid::LanguageIdentifier::matches&lt;/code&gt;&lt;/a&gt; function.
This function is able to compare two languages, and based on the seconds and third arguments, to treat them like range.
This allows comparing &lt;code&gt;en&lt;/code&gt; and &lt;code&gt;en-US&lt;/code&gt; easier.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;&lt;em&gt;But I don’t like JavaScript!&lt;/em&gt;, I hear you say.
And I’m with you. JavaScript is overused nowadays, and usually for bad purposes like annoying popup windows.
However, I try to keep my JavaScript code to a minimum. In fact, aside from &lt;code&gt;htmx&lt;/code&gt; and some event handlers, &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt; is mostly JavaScript free.
If you browse the web with disabled JavaScript, you will lose functionality. I’d like to have you as a customer, but I also care about a nice user experience.
So I always need to balance those to aspects.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;a-touch-of-ux&quot;&gt;A touch of UX&lt;/h3&gt;
&lt;p&gt;Some websites put the language switcher on the top; others at the bottom.
And some, hide it in obscure places, never to be found again.
I appreciate a UI that puts me, the user, in the center.
This is why I added a nice touch to the UI of JustFax.&lt;/p&gt;
&lt;p&gt;When your preferred language (the one that comes from the &lt;code&gt;Accept-Header&lt;/code&gt;), and your detected language (the actual subdomain language)—differ—I show you a nice, unintrusive popup:&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Language switch popup&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;680&quot; height=&quot;214&quot; src=&quot;/_astro/language_switch_ui.DBICrxhI_kbW72.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Language switch popup&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;You can either revert to your preferred language, or hide this popup, never to be shown again (for the next 30 days).&lt;/p&gt;
&lt;h2 id=&quot;step-threeactual-localization&quot;&gt;Step three—actual localization&lt;/h2&gt;
&lt;p&gt;I hope you are not tired, but we need to talk about how to actually localize your web pages.&lt;/p&gt;
&lt;p&gt;There exist a multitude of tools and frameworks to do so.
The lamest one look at a key, and it’s associated string.
So for example you could have a &lt;code&gt;en.toml&lt;/code&gt; with the content of:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;features-title&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = F&lt;/span&gt;&lt;span style=&quot;color:#FFFFFF&quot;&gt;eatures&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a &lt;code&gt;de.toml&lt;/code&gt; with the content of:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;features-title&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = F&lt;/span&gt;&lt;span style=&quot;color:#FFFFFF&quot;&gt;unktionen&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a magic &lt;code&gt;t()&lt;/code&gt; function in your templates, that will accept a key, and output its value.&lt;/p&gt;
&lt;p&gt;This is a lousy solution, if you aim for perfection (like I do).
Language is way more than just a collection of words.
One obvious example, is when enumerating things.
In English, you would say “Close &lt;strong&gt;one&lt;/strong&gt; tab” and “Close &lt;strong&gt;n&lt;/strong&gt; tabs” for any number of tabs other than one.
However, in many Slavic languages, you would use a different imperative for the word “tabs”.
In Polish for example, you would say “Zamknij kartę” for one tab; “Zamknij {$tabCount} karty” for few tabs (2, 3, and 4); and “Zamknij {$tabCount} kart” for many tabs (5, 6, and on).
In addition to imperatives, you might have different variants of translation based on gender, how lower/upper case is handled, etc.
So in short, languages are fun!&lt;/p&gt;
&lt;p&gt;There exists a tool made by GNU called &lt;code&gt;gettext&lt;/code&gt;.
It’s very robust tool for l10n.
The main feature of it, is that you will use an agreed function name, usually &lt;code&gt;_()&lt;/code&gt;, and the parameter of this function would be an English phrase: &lt;code&gt;_(&quot;Hello World&quot;)&lt;/code&gt;.
You’d then run the &lt;code&gt;gettext&lt;/code&gt; utility over your source code, and it will generate a &lt;code&gt;.po&lt;/code&gt; file to which it will export all the strings in your source code.
By using &lt;code&gt;POEdit&lt;/code&gt; GUI application, you will be able to translate those files, or give them to a translator.&lt;/p&gt;
&lt;p&gt;It’s a very robust tool that exists from 1990.
It’s written in C, and there are Rust &lt;a href=&quot;https://crates.io/search?q=gettext&quot;&gt;crates&lt;/a&gt; that interface with it.
It doesn’t pollute your code with obscure keys like &lt;code&gt;feature-title&lt;/code&gt;.
I, however, dislike it due to the fact that it needs to go over my source code, which means I need to have another pre-deploy task to perform.
Moreover, because the actual phrase is your key, when you find a typo, good luck fixing all your translations.&lt;/p&gt;
&lt;p&gt;Luckily, I was able to find &lt;a href=&quot;https://projectfluent.org/&quot;&gt;Project Fluent&lt;/a&gt; which is maintained by Mozilla, and is used in Firefox.
It addresses many of the issues with &lt;code&gt;gettext&lt;/code&gt;, and luckily for me, has a &lt;a href=&quot;https://github.com/projectfluent/fluent-rs&quot;&gt;Rust implementation&lt;/a&gt;.
The implementation is very raw though, and requires some juggling to make it work.
But, as you already understood, I’m very lucky and there is another crate, called &lt;a href=&quot;https://crates.io/crates/fluent-templates&quot;&gt;fluent-templates&lt;/a&gt;, which makes working with &lt;code&gt;fluent-rs&lt;/code&gt; way easier.
More luck on my side, it has a built-in implementation of the &lt;code&gt;t()&lt;/code&gt; function for the &lt;code&gt;tera&lt;/code&gt; templating engine that I use.
Inside a &lt;code&gt;tera&lt;/code&gt; template, I would call it like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;{{ t(key=&quot;hello-world&quot;, lang=LANG) }}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;LANG&lt;/code&gt; is a global variable defined in the &lt;code&gt;tera&lt;/code&gt; context, based on the detected language of the route.&lt;/p&gt;
&lt;p&gt;This article is already too long, so feel free to check &lt;code&gt;fluent-templates&lt;/code&gt; documentation.
It’s pretty straightforward.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Things like &lt;code&gt;l10n&lt;/code&gt; and &lt;code&gt;i18n&lt;/code&gt; are hard to get right.
Even giants like Google mess up sometimes.
I hope in this article I was able to cover the basic building blocks you would need for your own localized web applications in Rust.
Bear in mind that I didn’t even touch things like numbers, currencies and dates, which have their own caveats.
Maybe I’ll do one day in the future.&lt;/p&gt;</content:encoded></item><item><title>Building a Web App in Rust</title><link>https://kudmitry.com/articles/building-a-webapp-in-rust/</link><guid isPermaLink="true">https://kudmitry.com/articles/building-a-webapp-in-rust/</guid><pubDate>Sun, 12 Nov 2023 19:20:00 GMT</pubDate><content:encoded>&lt;p&gt;I remember when I first tried Rust, I fell in love.
It was like a breath of fresh air after JavaScript and NodeJS.
TypeScript made the experience way better, and I believe that if you still write pure JavaScript, you are dooming yourself.
I wrote about the importance of types &lt;a class=&quot;internal new&quot; href=&quot;/articles/it-is-better-to-be-typesafe-than-sorry/&quot;&gt;here&lt;/a&gt; and &lt;a class=&quot;internal new&quot; href=&quot;/articles/on-types-and-javascript-drama/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But back to Rust.
My first experience with Rust—aside from trying to build my own game using &lt;a href=&quot;https://crates.io/crates/ggez&quot;&gt;ggez&lt;/a&gt;, &lt;a href=&quot;https://crates.io/crates/bevy&quot;&gt;bevy&lt;/a&gt;, and &lt;a href=&quot;https://wgpu.rs/&quot;&gt;wgpu&lt;/a&gt;—was building a business logic layer in Rust which Electron app could call.
I wrote about my experience &lt;a class=&quot;internal new&quot; href=&quot;/articles/supercharge-nodejs-with-rust/&quot;&gt;here&lt;/a&gt; and &lt;a class=&quot;internal new&quot; href=&quot;/articles/native-rust-wasm/&quot;&gt;here&lt;/a&gt;.
This project, however, never saw light.
I’m not disappointed because I managed to craft two amazing pieces of content, and present them in one meetup and two international conferences.&lt;/p&gt;
&lt;p&gt;But I still wanted to write a web application in Rust, because as they say: &lt;em&gt;“anything that can be written in Rust, will be written in Rust”&lt;/em&gt;.
And on the 1st of November—I did!
I launched &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt;.
JustFax Online is a service that allows you to send a one-time fax without creating an account or committing to a subscription.
It’s a perfect tool for people who need to send an occasional fax.
And most important, in terms of technology, JustFax is a 100% Rust application.&lt;/p&gt;
&lt;h2 id=&quot;the-architecture&quot;&gt;The Architecture&lt;/h2&gt;
&lt;p&gt;Let’s talk about the architecture of JustFax.
It consists of two OS processes: the web server, and the dispatcher that is responsible for sending the faxes.&lt;/p&gt;
&lt;p&gt;The web server is written in &lt;a href=&quot;https://github.com/tokio-rs/axum&quot;&gt;axum&lt;/a&gt;, which is a very popular web framework.
&lt;code&gt;Axum&lt;/code&gt; is built on top of &lt;code&gt;tokio&lt;/code&gt;, which is and asynchronous runtime for Rust; &lt;code&gt;hyper&lt;/code&gt;, which is a low-level HTTP implementation in Rust; and &lt;code&gt;tower&lt;/code&gt;, which is a collection of reusable components for building networking clients and servers.&lt;/p&gt;
&lt;p&gt;On top of that, it uses the &lt;a href=&quot;https://github.com/Keats/tera&quot;&gt;tera&lt;/a&gt; templating engine, which has a similar syntax to Jinja2.
Storage is handled by SQLite, via the &lt;a href=&quot;https://github.com/rusqlite/rusqlite&quot;&gt;rusqlite&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;The dispatcher process is just an infinite loop that polls the database for changes.
There are also some 3rd party integrations, which I won’t go into details here.&lt;/p&gt;
&lt;p&gt;Let’s talk about my experience building a web application in Rust.&lt;/p&gt;
&lt;h2 id=&quot;the-good&quot;&gt;The Good&lt;/h2&gt;
&lt;p&gt;First, we will start with the good parts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Memory usage&lt;/strong&gt;.
This is obviously the first advantage you get from Rust.
I tweeted this image, shortly after deploying the first version.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Look Ma! A web app that doesn&apos;t take hundreds of MB of RAM&lt;a href=&quot;https://twitter.com/hashtag/buildinpublic?src=hash&amp;#x26;ref_src=twsrc%5Etfw&quot;&gt;#buildinpublic&lt;/a&gt; &lt;a href=&quot;https://t.co/Ymwt6sulTJ&quot;&gt;pic.twitter.com/Ymwt6sulTJ&lt;/a&gt;&lt;/p&gt;— Dmitry Kudryavtsev (@skwee357) &lt;a href=&quot;https://twitter.com/skwee357/status/1718471358726938732?ref_src=twsrc%5Etfw&quot;&gt;October 29, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;You can find the original post &lt;a href=&quot;https://twitter.com/skwee357/status/1718471358726938732&quot;&gt;on Twitter&lt;/a&gt; and &lt;a href=&quot;https://mstdn.social/@skwee357/111316204758588581&quot;&gt;on Mastodon&lt;/a&gt;.
Consider dropping a follow there as well ;)&lt;/p&gt;
&lt;p&gt;Yes, it’s true.
A web application + a job runner, all wrapped in a &lt;code&gt;supervisord&lt;/code&gt; in Docker container, that takes ~30MB of RAM.
Eat that NodeJS!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Safety&lt;/strong&gt;.
There is a saying in the Rust community that goes like this: &lt;em&gt;“If it complies, it runs.”&lt;/em&gt;
And in general, it’s true.
You can, obviously, run into runtime issues with deserialization of 3rd-party API responses, but that’s just a side effect of building dynamic applications.
Other than this, if you are able to compile it, it will run successfully, but not necessarily correctly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Async&lt;/strong&gt;.
People in Rust community like to shit on async Rust.
Coming from NodeJS, where async development is heavily embedded, I can’t seem to understand the hate.
Sure, async Rust is not as mature as NodeJS.
There are different macros to make asynchronous traits, but in general, I found async Rust to be nice.
I haven’t had the chance to debug asynchronous code, which I believe can be a pain.
And you always have the option to spawn a thread, should you need to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type safety&lt;/strong&gt;.
Oh, sweet Jesus!
Real type safety!
It feels incredible.
You no longer need to hunt stupid bugs where your UUID string, can have, accidentally, a non-uuid value.
A real, mature type system—is a welcoming addition to the development toolbox.&lt;/p&gt;
&lt;p&gt;Having said that, not all is rainbows and chocolate.&lt;/p&gt;
&lt;h2 id=&quot;the-bad&quot;&gt;The Bad&lt;/h2&gt;
&lt;p&gt;Rust is a complicated language.
I’m still far from understanding all its features.
And with such complexity, comes the downsides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The borrow checker&lt;/strong&gt;.
One of the biggest struggle for new Rustaceans—is the borrow checker.
In short: borrow checker is a Rust mechanism which is the heart of it’s safety and security.
This is what prevents Rust from leaking memory, or referencing a null pointer.&lt;/p&gt;
&lt;p&gt;Understanding the borrow checker is a complicated thing.
I must admit that if you know concepts such as &lt;em&gt;pass by references&lt;/em&gt;, &lt;em&gt;pass by value&lt;/em&gt;, &lt;em&gt;pointers&lt;/em&gt;, and when variables go out of scope—it helps a lot with understanding the borrow checker.
In addition to that, you barely have to touch the borrow checker by building a web application on top of &lt;code&gt;axum&lt;/code&gt;.
Unless you have complex data structures that hold references to other types/objects, most of the code will look, and read like a regular code in any other language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starting is rough&lt;/strong&gt;.
While &lt;code&gt;axum&lt;/code&gt; is very similar to your favorite web framework such as RoR, &lt;code&gt;fastify&lt;/code&gt;, or &lt;code&gt;django&lt;/code&gt;—it does have unique features to it.
Things like request extractors, the way middlewares work, are error handling.
This hurts DX and the speed at which you can deliver.&lt;/p&gt;
&lt;p&gt;I was familiar with &lt;code&gt;axum&lt;/code&gt; from a previous toy project I played with, but I must admit that I could have finished the project faster with NodeJS.
It could be related to the fact that I have more experience with NodeJS, or it could be a caveat of Rust development.
At that point, I no longer can answer this question reliably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compilation time&lt;/strong&gt;.
On my machine, compilation is relatively fast.
However, the moment I switched to a CI/CD pipeline, I’ve encountered a relatively long compilation times.
I &lt;a href=&quot;https://twitter.com/skwee357/status/1718452694786936944&quot;&gt;tweeted&lt;/a&gt; and &lt;a href=&quot;https://mstdn.social/@skwee357/111315913931289089&quot;&gt;tooted&lt;/a&gt; about the initial compilation times of ~650 seconds (which is more than 10 minutes) in a CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;I was able to reduce the compilation time to around 3 minutes, after removing the multi-stage docker build, and instead compiling the code inside the CI docker, as well as caching the dependencies.
As of today, the entire CI/CD pipeline from push to deployment takes about 6 minutes, which is not bad overall.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The ecosystem&lt;/strong&gt;.
Rust has a very big community around it, and a great ecosystem.
The community maintains a bunch of &lt;em&gt;arewe__yet&lt;/em&gt; websites, such as &lt;a href=&quot;https://www.arewewebyet.org/&quot;&gt;arewewebyet&lt;/a&gt; which list all the popular packages for a particular domain (in this case web development).&lt;/p&gt;
&lt;p&gt;Having said that, the ecosystem is not as big as NodeJS or Python.
Many semi-popular packages and libraries—will be missing.
Most examples will be given in either JavaScript, Python, Ruby, and even PHP—but will not have Rust.&lt;/p&gt;
&lt;p&gt;Many issues and problems you will encounter with, will require you to go through, mostly, unanswered &lt;a href=&quot;https://users.rust-lang.org/&quot;&gt;Rust community&lt;/a&gt; questions; in many cases you will have to read the source code of a particular crate in order to understand how it works.&lt;/p&gt;
&lt;p&gt;One great thing I love about the Rust community though—is that most packages are test covered, which serves as a great tool for examples.
And almost every popular package comes with a &lt;code&gt;examples&lt;/code&gt; folder inside the repo, where you can see popular use cases and build on top of them (&lt;a href=&quot;https://github.com/tokio-rs/axum/tree/main/examples&quot;&gt;example for &lt;code&gt;axum&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I really enjoyed building this project in Rust.
For a long time I looked for an excuse to launch a Rust project.
There is no particular reason for using Rust over, say, NodeJS.
But being and &lt;a href=&quot;https://www.kudmitry.com/projects/&quot;&gt;indie-hacker&lt;/a&gt;, I have the tendency to launch many projects without having $$ to throw on AWS or support teams.
Knowing that my application takes ~30MB of RAM, means I can run a few of them on a 1GB RAM shared VPS.
And Rust being a language that puts a lot of attention on safety, means I can run these projects without worrying of &lt;code&gt;cannot read &apos;foo&apos; of undefined&lt;/code&gt; errors, or my entire server is crashing due to an unexpected response from a third party service.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In case you need to send a fax, consider giving &lt;a href=&quot;https://justfaxonline.com&quot;&gt;JustFax Online&lt;/a&gt; a try.
The service is available in English, German, and French (with more languages to come).
It supports sending a fax in &lt;a href=&quot;https://justfaxonline.com/availability&quot;&gt;50 different countries&lt;/a&gt;.
You don’t need to create an account or commit to a subscription model.
It takes literally 2-minutes to send a fax.
And most importantly, you get to support a fellow indie-hacker.&lt;/p&gt;
&lt;p&gt;Follow me on &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;Twitter&lt;/a&gt; and &lt;a href=&quot;https://mstdn.social/@skwee357&quot;&gt;Mastodon&lt;/a&gt; to see more behind the scenes of my journey, as well as software engineering content.&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;</content:encoded></item><item><title>Typescript Monorepo with NPM workspaces</title><link>https://kudmitry.com/articles/npm-workspaces/</link><guid isPermaLink="true">https://kudmitry.com/articles/npm-workspaces/</guid><pubDate>Sun, 01 Oct 2023 23:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During the development of my recent project (more info on &lt;a href=&quot;https://kudmitry.com/projects/&quot;&gt;my website&lt;/a&gt;), I wanted to make it more modular.
It consists of 3 main components: &lt;code&gt;api&lt;/code&gt; which is the API server;
a process that is responsible for RSS feed discovery called &lt;code&gt;observer&lt;/code&gt;;
and a process responsible for sending out emails called &lt;code&gt;distributor&lt;/code&gt;.
They all depend on a database.&lt;/p&gt;
&lt;p&gt;I wanted all of them be independent executables.
Using a monorepo for such use case, is great.
But settings up dependencies between them, is complicated.&lt;/p&gt;
&lt;p&gt;Luckily, &lt;code&gt;npm&lt;/code&gt; 7 has support for workspaces, which simplify the management of monorepos.
Not only that, but using workspaces also helps you to keep your &lt;code&gt;node_modules&lt;/code&gt; more lean (as much as possible).&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;Let’s say you have 3 projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;infra&lt;/code&gt; which responsible for working with the database&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api&lt;/code&gt; which is your API server&lt;/li&gt;
&lt;li&gt;&lt;code&gt;worker&lt;/code&gt; which is some kind of asynchronous processing worker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You want to keep them all separated, with their own set of dependencies.
Both &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt; need to depend on &lt;code&gt;infra&lt;/code&gt;.
You could just create 3 different projects, publish the &lt;code&gt;infra&lt;/code&gt; on some internal repository, and install it as a dependency for &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt;.
But this really complicates things.&lt;/p&gt;
&lt;p&gt;Instead, you can setup a workspace using &lt;code&gt;npm&lt;/code&gt; (we will see in a moment how to do that).
On top of all that, &lt;code&gt;npm&lt;/code&gt; will optimize your &lt;code&gt;node_modules&lt;/code&gt;.
So if both &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt; depend on the same version of &lt;code&gt;uuid&lt;/code&gt; package, instead of installing the package two times, it will be installed only once.&lt;/p&gt;
&lt;p&gt;The general project structure also looks different:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; package-lock.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; packages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; ├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; ├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; worker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; └──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; infra&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; tsconfig.build.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; tsconfig.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get only one &lt;code&gt;node_modules&lt;/code&gt; at the top level.
All dependencies will be there, including symlinks to your internal packages:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;.node_modules/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; -&gt; &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;../packages/api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; worker&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; -&gt; &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;../packages/worker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; infra&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; -&gt; &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;../packages/infra&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So this means you can install them just like regular packages!
Let’s dive in.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-npm-workspaces&quot;&gt;Setting up npm workspaces&lt;/h2&gt;
&lt;p&gt;We are also going to setup typescript along the way, as there are some caveats.&lt;/p&gt;
&lt;p&gt;Inside your top level folder, you will need to create a &lt;code&gt;package.json&lt;/code&gt; (either manually, or via &lt;code&gt;npm init&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;my-app&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;private&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;scripts&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;workspaces&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;packages/*&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;devDependencies&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;@tsconfig/recommended&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;^1.0.2&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;@types/node&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;^20.6.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;ts-node&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;^10.9.1&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;typescript&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;^5.2.2&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most important line is &lt;code&gt;&quot;workspaces&quot;: [&quot;packages/*&quot;]&lt;/code&gt;, which instructs &lt;code&gt;npm&lt;/code&gt; to treat this package as a workspaces root.
All the actual packages will be inside the &lt;code&gt;./packages&lt;/code&gt; folder.
By the way, you can name this folder whatever you want.
I also added some dev dependencies for typescript.&lt;/p&gt;
&lt;p&gt;While we are inside the root folder, it’s also a good time to create some typescript boilerplate.&lt;/p&gt;
&lt;p&gt;First, we need a base &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;extends&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;@tsconfig/recommended&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;compilerOptions&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;incremental&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;target&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;es2019&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;module&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;commonjs&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;declaration&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;sourceMap&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;composite&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;strict&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;moduleResolution&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;esModuleInterop&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;skipLibCheck&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;forceConsistentCasingInFileNames&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the base typescript configuration for all the packages.
Since we want to keep everything consistent throughout the monorepo, we will change any project wide settings there.
Next, we need another config, this time for building the entire monorepo: &lt;code&gt;tsconfig.build.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;files&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;references&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      &quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;packages/infra&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      &quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;packages/api&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      &quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;packages/worker&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And while we are here, let’s also add a build script to the root &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;build&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;tsc --build --verbose tsconfig.build.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, when we run &lt;code&gt;npm run build&lt;/code&gt; in the root of the monorepo, the entire app will be built.&lt;/p&gt;
&lt;h3 id=&quot;creating-a-package&quot;&gt;Creating a package&lt;/h3&gt;
&lt;p&gt;Now, we need to create individual packages.
This can be done either manually, or via &lt;code&gt;npm init&lt;/code&gt;.
For example, in order to create the &lt;code&gt;infra&lt;/code&gt; package, we will execute &lt;code&gt;npm init --workspace packages/infra -y&lt;/code&gt;.
This will create a default &lt;code&gt;package.json&lt;/code&gt; inside the &lt;code&gt;packages/infra&lt;/code&gt; directory.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I recommend you use scoping for your project.
Scoping helps you avoid any confusion with existing packages.
In order to use scoping, when calling &lt;code&gt;npm init&lt;/code&gt; provide a flag &lt;code&gt;--scope @mycompany&lt;/code&gt;.
This way, all your packages will be prefixed with &lt;code&gt;@mycompany&lt;/code&gt;, and won’t cause any confusion.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here, we work regularly.
We install dependencies as if it was a standalone package.
Any &lt;code&gt;npm&lt;/code&gt; script can be executed from inside the &lt;code&gt;packages/infra&lt;/code&gt; directory, or from the root directory using &lt;code&gt;npm run &amp;#x3C;script-name&gt; --workspace packages/infra&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another tip.
If you want to execute a script across all packages, you can also use &lt;code&gt;npm run &amp;#x3C;script-name&gt; --workspaces&lt;/code&gt;.
This will iterate over all the packages in a workspace, and execute the said script (or emit error if no such script is defined).
This is very handy for running tests, for example.&lt;/p&gt;
&lt;p&gt;In order to install a dependency inside a package, we will execute &lt;code&gt;npm install --save uuid --workspace/infra&lt;/code&gt;.
However, as you will notice, there won’t be a &lt;code&gt;node_modules&lt;/code&gt; inside &lt;code&gt;packages/infra&lt;/code&gt;.
Instead, all the dependencies will be put in a top level &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, let’s answer the question of how to share &lt;code&gt;infra&lt;/code&gt; with both &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt;.
Repeat the above steps in order to create &lt;code&gt;packages/api&lt;/code&gt; and &lt;code&gt;packages/worker&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And now, when you have all of them ready, just execute &lt;code&gt;npm install --save @mycompany/infra --workspace packages/api&lt;/code&gt; (and repeat it for &lt;code&gt;worker as well&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Here, scoping comes into play.
The global &lt;code&gt;npm&lt;/code&gt; registry has a package called &lt;code&gt;infra&lt;/code&gt;, so the above command is ambiguous without scoping.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;setting-up-typescript&quot;&gt;Setting up typescript&lt;/h3&gt;
&lt;p&gt;Lastly, we want to set up typescript for the individual packages.
Inside every package, create a &lt;code&gt;tsconfig.json&lt;/code&gt; that looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;extends&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;../../tsconfig.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;compilerOptions&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;rootDir&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;outDir&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;dist&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;paths&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      &quot;@mycompany/*&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;./packages/*&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;references&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      &quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;../infra&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we extend the global &lt;code&gt;tsconfig.json&lt;/code&gt; that we created.
This is necessary in order to keep the general guidelines among all packages—the same.&lt;/p&gt;
&lt;p&gt;Next, we override some &lt;code&gt;compilerOptions&lt;/code&gt;.
It’s needed in order to tell typescript where are the source files and where is the output directory.
We can’t include it in the base config, since it’s relative to the package path.&lt;/p&gt;
&lt;p&gt;On the &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt; packages, we need two more things: &lt;code&gt;paths&lt;/code&gt; and &lt;code&gt;references&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;paths&lt;/code&gt; allows us to use scoped imports such as &lt;code&gt;import x from @mycompany/infra&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;references&lt;/code&gt; allows us to use the &lt;code&gt;.d.ts&lt;/code&gt; files of the dependent package.
If you want to learn more about &lt;code&gt;references&lt;/code&gt;, consider reading the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/project-references.html&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;The &lt;code&gt;infra&lt;/code&gt; package does not need to have &lt;code&gt;paths&lt;/code&gt; and &lt;code&gt;references&lt;/code&gt; since it does not depend on any internal packages.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, when we execute &lt;code&gt;npm run build&lt;/code&gt;, all the packages will be built.
According to our internal &lt;code&gt;tsconfig.json&lt;/code&gt; files, the output will be placed inside &lt;code&gt;packages/&amp;#x3C;package-name&gt;/dist&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;caveats&quot;&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While workspaces in &lt;code&gt;npm&lt;/code&gt; are great to keep things clean, they come with some caveats.&lt;/p&gt;
&lt;p&gt;One problem I’ve read about online, but haven’t encountered myself, is improper dependency resolution.
Since all dependencies are flattened and put into a global &lt;code&gt;node_modules&lt;/code&gt; directory, some people reported that it can cause bugs when the wrong dependency is imported.&lt;/p&gt;
&lt;p&gt;Another downside of this approach, is that you won’t be able to produce atomic units for deployment.
Since &lt;code&gt;node_modules&lt;/code&gt; is shared among all packages, it needs to be included with each and every executable unit.&lt;/p&gt;
&lt;p&gt;Consider a scenario where &lt;code&gt;api&lt;/code&gt; depends on a lot of packages, but &lt;code&gt;worker&lt;/code&gt; depends only on &lt;code&gt;@mycompany/infra&lt;/code&gt; (which in turn depends on some database package).
In order to deploy &lt;code&gt;worker&lt;/code&gt; independently, you will have to copy the entire &lt;code&gt;node_modules&lt;/code&gt; along with it.
This is less than ideal if you deploy &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;worker&lt;/code&gt; on separate docker containers or machines.
However, if you have one Docker container that runs both using &lt;code&gt;supervisord&lt;/code&gt; or &lt;code&gt;pm2&lt;/code&gt;—it’s not a problem.&lt;/p&gt;</content:encoded></item><item><title>On APIs and their responses</title><link>https://kudmitry.com/articles/on-apis-and-responses/</link><guid isPermaLink="true">https://kudmitry.com/articles/on-apis-and-responses/</guid><pubDate>Mon, 25 Sep 2023 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since the dawn of &lt;del&gt;times&lt;/del&gt; the web, humans created CRUD APIs.
And we were instructed that modification verbs should return the modified resource in response.
But, should they?&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;A basic CRUD API consists of 5 verbs.
Let’s go over them quickly.&lt;/p&gt;
&lt;h2 id=&quot;the-5-verbs&quot;&gt;The 5 verbs&lt;/h2&gt;
&lt;h3 id=&quot;get&quot;&gt;GET&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;GET&lt;/code&gt; verb is used to retrieve the resource, or a collection of resources.
It should not make any modification on the server, and therefor it’s idempotent.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Before moving on, we need to talk about &lt;strong&gt;idempotent verbs&lt;/strong&gt;.
In regard to HTTP verbs, a verb is idempotent only if subsequent calls to the same endpoint, with the same verb—will produce the same result.
&lt;code&gt;GET&lt;/code&gt; is idempotent, because if I execute &lt;code&gt;GET&lt;/code&gt; method on a particular endpoint, let’s say &lt;code&gt;/post/on-apis-and-responses&lt;/code&gt;—I’ll always get the same result, and the state on the server does not change.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;post&quot;&gt;POST&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;POST&lt;/code&gt; verb is used to create a resource on the server.
It also can be used to create a collection of resources.
From what you understood, &lt;code&gt;POST&lt;/code&gt; changes the state on the server, and is not idempotent.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Why &lt;code&gt;POST&lt;/code&gt; is not idempotent?
Because subsequent &lt;code&gt;POST&lt;/code&gt; request will either fail, in case of duplicate identifier, or will create another, &lt;strong&gt;different&lt;/strong&gt;, resource.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;put&quot;&gt;PUT&lt;/h3&gt;
&lt;p&gt;Contrary to &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; is used to &lt;strong&gt;replace&lt;/strong&gt; a resource, or a collection or resources.
Like &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; modified the state on the server.
However, unlike &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; is idempotent.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Why &lt;code&gt;PUT&lt;/code&gt; is idempotent?
Since &lt;code&gt;PUT&lt;/code&gt; fully replaces the resource, subsequent calls will end-up with the same state—the replaced resource.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;patch&quot;&gt;PATCH&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PATCH&lt;/code&gt; is not a well known verb.
Some confuse &lt;code&gt;PATCH&lt;/code&gt; with &lt;code&gt;PUT&lt;/code&gt;, but there is a difference.
While &lt;code&gt;PUT&lt;/code&gt; replaces the entire resource, &lt;code&gt;PATCH&lt;/code&gt; is designed to update parts of a resource.&lt;/p&gt;
&lt;p&gt;To give you a better example, imagine we have the following resource:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; resource&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;contact@kudmitry.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;@skwee357&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to update the email, I have two options.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can issue a &lt;code&gt;PUT&lt;/code&gt; request.
But this will require me to submit both &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt; fields, because &lt;code&gt;PUT&lt;/code&gt; used to replace the resource.&lt;/li&gt;
&lt;li&gt;I can issue a &lt;code&gt;PATCH&lt;/code&gt; request.
This way, I can submit a partial JSON that will contain only the new email.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;PATCH&lt;/code&gt;, as you probably guessed, modifies the state on the server.
However, if you assumed that &lt;code&gt;PATCH&lt;/code&gt; is idempotent, just like &lt;code&gt;PUT&lt;/code&gt;, you are wrong.
I won’t go into details here, but you can check this 8-year-old &lt;a href=&quot;https://stackoverflow.com/questions/28459418/use-of-put-vs-patch-methods-in-rest-api-real-life-scenarios&quot;&gt;StackOverflow&lt;/a&gt; question, where I asked about the same thing.&lt;/p&gt;
&lt;h3 id=&quot;delete&quot;&gt;DELETE&lt;/h3&gt;
&lt;p&gt;Last, but not least, is &lt;code&gt;DELETE&lt;/code&gt;.
It’s used to delete a resource from the server.
It modifies the state on the server, and is idempotent.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Why &lt;code&gt;DELETE&lt;/code&gt; is idempotent?
While the first call to &lt;code&gt;DELETE&lt;/code&gt; will, most likely, return &lt;code&gt;200 Ok&lt;/code&gt;—subsequent calls will, most likely, return &lt;code&gt;404 Not Found&lt;/code&gt;.
However, it’s not about the response.
The state on the server remains the same—the resource no longer exists.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;on-post-put-and-patch-and-their-responses&quot;&gt;On POST, PUT, and PATCH and their responses&lt;/h2&gt;
&lt;p&gt;There is a verbal agreement that &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;PATCH&lt;/code&gt; operations should return the created/updated resource.
In the rest of this article, I want to provide reasons why they &lt;strong&gt;shouldn’t&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Everything I’ll talk about, applies to standard CRUD APIs.
If you develop a specialized API that does not adhere to the CRUD structure—do what you want, and what suits your needs best.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;reason-1-api-consistency&quot;&gt;Reason 1: API consistency&lt;/h3&gt;
&lt;p&gt;Consistent APIs are easier to work with.
It doesn’t matter if you plan on third-parties to integrate with your API, or it’s used internally by your frontend/mobile teams.&lt;/p&gt;
&lt;p&gt;By returning a newly created resource inside &lt;code&gt;POST&lt;/code&gt; request—you will have to return a response in all &lt;code&gt;POST&lt;/code&gt; requests.
Otherwise, the people who integrate with your API, will have to handle endpoint-by-endpoint use-cases.&lt;/p&gt;
&lt;p&gt;Returning response in every &lt;code&gt;POST&lt;/code&gt; endpoint is, however, unfeasible.
Why?
Here are some examples.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What would your return on &lt;code&gt;POST&lt;/code&gt; endpoint that accepts batch data from CSV?
Let’s say you are able to process the CSV in-place, without delayed processing.
Are you going to return all the newly created resources?
What if there are hundreds of them?
Do you return them paginated?&lt;/li&gt;
&lt;li&gt;What would you return on a &lt;code&gt;POST&lt;/code&gt; endpoint that schedules an async job?
Let’s say you send s CSV, but instead of parsing it in-place, you schedule the parsing for a later stage using some queue.
Do you return a job object?
A job ID?
Do you even want to expose your API consumers to the concept of asynchronous processing?&lt;/li&gt;
&lt;li&gt;Imagine you have a resource such as blog post.
This blog post can have tags.
After issuing a &lt;code&gt;PATCH&lt;/code&gt; request to add a new tag, what would you return?
Do you return the entire blog post object, or only the array of tags?
Or maybe the newly created tag?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I demonstrated, there are some ambiguities you need to solve in order to support returning the resource on create/update.
This complicates your API, and makes it harder for your consumers.&lt;/p&gt;
&lt;p&gt;What is the alternative?
There is one, but first, let’s discuss some more issues with returning resources.&lt;/p&gt;
&lt;h3 id=&quot;reason-2-offline-support&quot;&gt;Reason 2: Offline support&lt;/h3&gt;
&lt;p&gt;One reason to return the resource on create/update operations—is to avoid the additional &lt;code&gt;GET&lt;/code&gt; call to retrieve the newly created/updated resource, that the client will have to do.
However, when designing an API to be consumed by a client that can work in offline mode, the client needs to backfill the newly created resource anyway.&lt;/p&gt;
&lt;p&gt;When a user creates a resource, but the application is offline, you want to have optimistic flow.
The UI updates accordingly, but the resource is queued to be created when the client goes back online.
In that case, the application already knows the structure of the &lt;code&gt;POST&lt;/code&gt; response.
Hence, it’s unnecessary to return anything when issuing create/update.&lt;/p&gt;
&lt;p&gt;When the client will be back online, it will execute all the create/update operations to let the server know what happened, and will issue a &lt;code&gt;GET&lt;/code&gt; request to update the view for the user.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But Dmitry, I don’t care about offline!&lt;/em&gt;
Too bad.
But let’s say you really don’t care about offline.
In today’s world, of zero patience, users expect stuff to happen instantly.
And even if you issue a &lt;code&gt;POST&lt;/code&gt; request, and wait for a response from it, there could be a noticeable latency that your users won’t like.&lt;/p&gt;
&lt;p&gt;One way to fix this latency, is to do an optimistic update.
The user fills the form, and click submit.
Your UI framework creates a dummy resource and immediately appends it to the list of resources, as if it was created successfully, hence updating the UI.
The user is happy.&lt;/p&gt;
&lt;p&gt;In the background, you issue a &lt;code&gt;POST&lt;/code&gt; request to create the resource on the server.
There is a high probability that the request succeeds, hence the name—&lt;em&gt;optimistic&lt;/em&gt; update.
If it fails, we can notify the user via a pop-up, or retry again up to a certain number of retires.&lt;/p&gt;
&lt;h3 id=&quot;reason-3-it-violates-dry&quot;&gt;Reason 3: It violates DRY&lt;/h3&gt;
&lt;p&gt;DRY, which is an abbreviation for &lt;em&gt;don’t repeat yourself&lt;/em&gt;, is a principle that states that your code should be free of repetitions.
Repetition make the code hard to maintain, as now there are few places that do the same, or depend on the same method.&lt;/p&gt;
&lt;p&gt;By introducing a response to create/update operations, you violate DRY, because you have to return the same resource as the one you return in &lt;code&gt;GET&lt;/code&gt;.
And if the resources are different between create/update and &lt;code&gt;GET&lt;/code&gt; calls, your API becomes inconsistent, which violates the first reason about API consistency.&lt;/p&gt;
&lt;h3 id=&quot;reason-4-it-violates-srp&quot;&gt;Reason 4: It violates SRP&lt;/h3&gt;
&lt;p&gt;SRP, which is an abbreviation for &lt;em&gt;single responsibility principle&lt;/em&gt;, states that each module in your code should be responsible for one action.
&lt;code&gt;GET&lt;/code&gt; returns the resource.
&lt;code&gt;DELETE&lt;/code&gt; removes the resource.
But when you introduce response to create/update operations, they no longer have single responsibility.
This can get complicated.&lt;/p&gt;
&lt;p&gt;Consider a use-case of adding a new tag to a blog post.
The entire logic (excluding authentication and validation), can be narrowed to one particular SQL:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;INSERT INTO&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; blog_post_tags (post_id, tag) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;VALUES&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ($&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, $&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;ON&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; CONFLICT &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;IGNORE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So when your &lt;code&gt;POST&lt;/code&gt; operation finishes, you don’t hold the entire blog post resource.
Therefore, in-order to return a proper response during &lt;code&gt;POST&lt;/code&gt;, you either need to call &lt;code&gt;GET&lt;/code&gt; on the same resource internally (many frameworks support issuing internal HTTP requests).
Or you need to refactor the resource retrieval, and serialization, to an external function which both the &lt;code&gt;GET&lt;/code&gt; and the &lt;code&gt;POST&lt;/code&gt; handlers will call.&lt;/p&gt;
&lt;p&gt;Now, they need not only create or update the said resource, but also serialize it in the same way as &lt;code&gt;GET&lt;/code&gt; would, and return it to the client.&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-instead&quot;&gt;What to do instead&lt;/h2&gt;
&lt;p&gt;Well, nothing!
You just don’t return anything.
The same way we do &lt;code&gt;DELETE&lt;/code&gt; requests, which return &lt;code&gt;200 OK&lt;/code&gt;.
However, for resource creation you should use &lt;code&gt;201 Created&lt;/code&gt; or &lt;code&gt;202 Accepted&lt;/code&gt; if the resource will be created asynchronously.&lt;/p&gt;
&lt;p&gt;If you want a really nice API design, you can return a header named &lt;code&gt;Content-Location&lt;/code&gt; which will point the client to the location of the newly created resource.
This creates a very nice decoupling inside your code, because retrieving the resource becomes a matter of reading this header, rather than hard-coding a specific end-point.&lt;/p&gt;
&lt;p&gt;So, instead of&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/task&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; task&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`/task/&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It becomes less coupled:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/task&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; task&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;content-location&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This, obviously, won’t work for batch operations.
While you can use another header, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link&quot;&gt;&lt;code&gt;Link&lt;/code&gt;&lt;/a&gt;, this will quickly get ugly if you have more than a few resources.
You can return a &lt;code&gt;Content-Location&lt;/code&gt; header that points to the collection, instead of an individual entity.
However, I think it’s better to not return anything.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But what about the subsequent &lt;code&gt;GET&lt;/code&gt; request?&lt;/em&gt;
Well, for starters, if you rely on optimistic update (and you should), your UI already reflects the change.
There is no need to fetch the created resource.&lt;/p&gt;
&lt;p&gt;In general, &lt;code&gt;GET&lt;/code&gt; on collection should be performed only once—when the user opens the list of tasks/todos/whatever.
&lt;code&gt;GET&lt;/code&gt; on a particular resource, should be performed when the user opens the page of that particular resource.
In all other cases, you better rely on optimistic update, especially if you have mobile clients with offline capabilities.&lt;/p&gt;
&lt;p&gt;And if you really have to fetch the resource, just issue the &lt;code&gt;GET&lt;/code&gt; request.
It’s not that big of deal.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s it.
That’s all I wanted to share with you today.&lt;/p&gt;
&lt;p&gt;Let me know what do you think about this approach.
I’m available via Email, Twitter, Mastodon, and (for the brave corporate folks among you) LinkedIn.&lt;/p&gt;</content:encoded></item><item><title>On types and JavaScript drama</title><link>https://kudmitry.com/articles/on-types-and-javascript-drama/</link><guid isPermaLink="true">https://kudmitry.com/articles/on-types-and-javascript-drama/</guid><pubDate>Sat, 09 Sep 2023 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this week, the entire JavaScript community was in shambles over a controversial decision in a popular open-source project.&lt;/p&gt;
&lt;h2 id=&quot;what-happened&quot;&gt;What happened?&lt;/h2&gt;
&lt;p&gt;David Heinemeier Hansson (known as DHH)—who is the creator of Ruby on Rails (among many other things)—approved a &lt;a href=&quot;https://github.com/hotwired/turbo/pull/971&quot;&gt;PR&lt;/a&gt; that removes Typescript from turbo framework.&lt;/p&gt;
&lt;p&gt;This decision upset a lot of people, and created a big controversy.
It even reached trolling inside the project’s issues/pull requests sections on GitHub.
Many people, some of them never even heard about turbo, were really upset.&lt;/p&gt;
&lt;p&gt;Here are 3 lessons we can learn from it.&lt;/p&gt;
&lt;h3 id=&quot;lesson-1-caring-less&quot;&gt;Lesson #1: Caring less&lt;/h3&gt;
&lt;p&gt;While DHH is a popular figure inside the development community, he is just a regular guy with his own opinions and views on software development.
The fact that he decided to go back to vanilla JavaScript—should not, in any way, affect your development preferences.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Clarification&lt;/em&gt;: I don’t suggest you dismiss any opinion which does not align with yours.
It’s important to listen to other opinions and review your development preferences from time-to-time.
What I &lt;strong&gt;do&lt;/strong&gt; suggest is to not follow, blindly, opinions of other people just because they are well known in the industry.&lt;/p&gt;
&lt;h3 id=&quot;lesson-2-how-to-not-treat-the-community&quot;&gt;Lesson #2: How to (not) treat the community&lt;/h3&gt;
&lt;p&gt;Many people expressed a very valid concern that I agree with.
Turbo is an open-source package.
And while DHH can do whatever he wants, the success of turbo is thanks to a lot of contributors.
Some of them had open PRs, not yet merged, that are now obsolete.
I didn’t dig too deep, because I don’t like drama, but from my understanding—the change was not communicated to contributors.
I can see why active contributors are getting upset—they did not get the respect that they deserve.&lt;/p&gt;
&lt;p&gt;It seems like this is a common pattern in some open-source projects.
Project that are led by people who care only about themselves and their agenda.
We should remember that the success of OSS depends a lot on the community, and the people who, often times without any monetary gains, dedicate their time for the project.&lt;/p&gt;
&lt;h3 id=&quot;lesson-3-types-are-important&quot;&gt;Lesson #3: Types are important&lt;/h3&gt;
&lt;p&gt;I strongly disagree with DHHs decision.
Going back to vanilla JavaScript—is a big step backwards.
It is now up to DHH to sort out his relationships with contributors, but I suggest you do not follow his lead.
Type safety, even at compile time, is a big guarantee for maintainable, and correct code.&lt;/p&gt;
&lt;p&gt;While DHHs arguments were simpler code, and less type acrobatics&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;—in reality, I believe it will bite him back on day.
The amount of &lt;code&gt;can not read foo of undefined&lt;/code&gt; errors, that I was dealing with back in the days of vanilla JavaScript, is too much.
And considering the fact that you need to have a healthy development cadence, dealing with such errors—hurts DX a lot.&lt;/p&gt;
&lt;p&gt;I suggest you check one of my old blog posts about type safety titled &lt;a class=&quot;internal new&quot; href=&quot;/articles/it-is-better-to-be-typesafe-than-sorry/&quot;&gt;It&apos;s better to be (type)safe than sorry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the meantime, stay safe (pun intended) and enjoy the weekend.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01&quot;&gt;https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>The Maya Civilization</title><link>https://kudmitry.com/articles/maya-tikal-yaxha/</link><guid isPermaLink="true">https://kudmitry.com/articles/maya-tikal-yaxha/</guid><pubDate>Sun, 03 Sep 2023 22:00:00 GMT</pubDate><content:encoded>&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Me, looking at Temple I in Tikal National Park&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;1067&quot; src=&quot;/_astro/maya-tikal.Co5dVcoY_lbK4d.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Me, looking at Temple I in Tikal National Park&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Coincidentally, but Guatemala turns out to be the cradle of the Mayan civilization.
From there, Maya expanded north to Mexican regions of Chiapas and Yucatán, and south down to the border of Costa Rica.&lt;/p&gt;
&lt;p&gt;Armed with that knowledge, I visited two national parks: Yaxha (pronounced as Yash-ah), and Tikal (the biggest and most powerful Mayan city).&lt;/p&gt;
&lt;p&gt;Mayan’s civilization had vast knowledge in astronomy, architecture, engineering, agriculture, and mathematics.
And I want to introduce you to Maya through some numbers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 - Mayans were the first civilization to use the zero.
This allowed them to count to very high numbers, as well as perform complex calculations.&lt;/li&gt;
&lt;li&gt;1 - the one god that Maya believed in.
Contrary to popular belief, Mayans were monotheistic.
The other “gods” such as fire, crops, water, etc—are in reality energies.&lt;/li&gt;
&lt;li&gt;3 - the 3 worlds the Maya believed in: underworld, our world, and next life.
Maya believed in life after death.&lt;/li&gt;
&lt;li&gt;5 - the 5 ceremonial days per year the Maya had.
In addition to that, the Mayan calendar had 18 months with 20 days per month.
If you do the math, together with the 5 ceremonial days, you will see that Mayan year was 365 days.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are still descendants of Maya living in the highlands of Guatemala.
There are 22 Mayan languages left.
But the knowledge they possessed—is long gone.
Big thanks to bishop Diego de Landa from Spain, who burned the majority of the Mayan books.&lt;/p&gt;
&lt;p&gt;Why did the Mayan disappear? 3 main reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Over population.
Tikal alone believed to have population between 1.5 to 2 million.&lt;/li&gt;
&lt;li&gt;Deforestation.
Sustaining big cities, required burning wood.
Maya were among the first civilizations to use mortar, which required a lot of fire to make.&lt;/li&gt;
&lt;li&gt;Deforestation lead to droughts, which lead to diseases, and civil wars.
In addition to that, the soil was exhausted and not suitable for crops anymore.
Most Mayan cities were abandoned, and survivors moved to highlands.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was a great civilization that existed for more than 3000 years (from 2000 B.C. to 1519 A.D.).&lt;/p&gt;
&lt;p&gt;The slow demise of the Mayan civilization, and the problem they faced—might be sign to what awaits us.&lt;/p&gt;</content:encoded></item><item><title>Three months of nomading</title><link>https://kudmitry.com/articles/three-months-of-nomading/</link><guid isPermaLink="true">https://kudmitry.com/articles/three-months-of-nomading/</guid><pubDate>Fri, 01 Sep 2023 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s been 3 months of #nomading the world and #solopreneuring. Here is what I learned.&lt;/p&gt;
&lt;h2 id=&quot;1-communities-are-hard-to-build&quot;&gt;1. Communities are hard to build&lt;/h2&gt;
&lt;p&gt;Communities rely on a mass of people in order to operate well.
Websites like Reddit, are useless if you have 2 users.
Focus on building a product you can sell to one person.&lt;/p&gt;
&lt;h2 id=&quot;2-build-it-and-they-will-come--is-a-lie&quot;&gt;2. Build it, and they will come — is a lie&lt;/h2&gt;
&lt;p&gt;There are endless amounts of tools out there.
Chances are you never heard about them.
Building a profitable product, requires you to either do ads (which I don’t like), or have a brand/following (newsletter, LinkedIn, twitter, mastodon).&lt;/p&gt;
&lt;h2 id=&quot;3-solopreneurship-is-not-only-coding&quot;&gt;3. Solopreneurship is not only coding&lt;/h2&gt;
&lt;p&gt;It’s design, marketing, promotion, customer support, and sales.
If you don’t want to do all that, don’t become a solopreneur.&lt;/p&gt;
&lt;h2 id=&quot;4-consistency-wins&quot;&gt;4. Consistency wins&lt;/h2&gt;
&lt;p&gt;If you keep going, you will eventually win.
Failures are inevitable, but they are temporary as long as you keep going.
You only fail when you quit.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I’m sitting in an airport once again, this time it’s a domestic flight to see Mayan ruins.
And I have plans to keep on going no matter how hard it is.
I really don’t want to come back to a traditional workplace anymore.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Me at an Airport in Guatemala&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;1066&quot; src=&quot;/_astro/three-months-nomading.0Qp4ody4_1g21B5.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Me at an Airport in Guatemala&lt;/figcaption&gt;&lt;/figure&gt;</content:encoded></item><item><title>Own your content</title><link>https://kudmitry.com/articles/own-your-content/</link><guid isPermaLink="true">https://kudmitry.com/articles/own-your-content/</guid><pubDate>Wed, 30 Aug 2023 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the past years, we’ve witnessed a rise of new type of social media platforms.
Platforms that compete for the ownership of your content.
And the attention of your consumers.&lt;/p&gt;
&lt;h2 id=&quot;from-owning-to-renting&quot;&gt;From owning to renting&lt;/h2&gt;
&lt;p&gt;Before I’ll talk about why, and how you should own your content—let me briefly cover the transition we’ve made from owning stuff, to renting stuff.
Feel free to skip to &lt;a href=&quot;#why-you-should-own-your-content&quot;&gt;#Why you should own your content&lt;/a&gt; if you are not interested in a bit of history.&lt;/p&gt;
&lt;p&gt;As consumers, we used to own the things we paid for.
If I bought a DVD with The Matrix movie—it’s mine forever.
I can re-watch it as many times as I want.
I can create copies from it, convert it to different formats, and watch it on different computers &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.
The only way someone can take it from me—is by stealing, which is punishable by law.&lt;/p&gt;
&lt;p&gt;I remember the days when we used to buy new music albums of our favorite bands.
These days are over, because everything is now for rent.
I was very surprised when I learned that even the Kindle books I buy—are rented to me.
Yes, I knew that Kindle books are protected by DRM, and it’s hard (not impossible) to read them on a non kindle device.
It’s not perfect, but I’m okay with such arrangement.&lt;/p&gt;
&lt;p&gt;However, when I checked the terms and conditions of Kindle books, I found out that the content is not sold, but rather licensed to us.
I tweeted about it back in December.&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I kind of swallowed the pill that I no longer own music or video content. I don&apos;t watch that much TV shows or movies, and anyway I rarely rewatch them. And so losing video content due to platform&apos;s removal - is sad, I won&apos;t cry over it. But I discovered something interesting.👇🧵&lt;/p&gt;— Dmitry Kudryavtsev (@skwee357) &lt;a href=&quot;https://twitter.com/skwee357/status/1605896638974795776?ref_src=twsrc%5Etfw&quot;&gt;December 22, 2022&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;And in case you are not aware: your Netflix shows, Spotify playlists, etc—are all rented to you.
There are cases when content that was available in your geographic region, or even content &lt;strong&gt;you bought&lt;/strong&gt;—was removed from the platform.&lt;/p&gt;
&lt;p&gt;But I don’t want to ramble too much about content ownership from a consumer perspective.
It is important, don’t get me wrong.
But I’m here to talk about content ownership as a producer, which also participated in the “rental revolution”.&lt;/p&gt;
&lt;h2 id=&quot;why-you-should-own-your-content&quot;&gt;Why you should own your content&lt;/h2&gt;
&lt;p&gt;Content was, and still is, one of the biggest assets you can create.
This blog is visited by hundreds of people daily—because it has the knowledge that I possess, presented in textual format.
Content is the reason we have the Internet in the form we have it today.
And it’s incredible.&lt;/p&gt;
&lt;p&gt;I’ve learned so much from other people’s blogs and posts.
And I’m doing my best to spread my knowledge further, to more people.
And while I want to do it on my terms, there are companies that want to monopolize this market.
They create tools for easy content creation, and distribution—but in return they gather most of the rewards.&lt;/p&gt;
&lt;p&gt;These companies will lock content behind a pay-wall.
They will create their services is such way to make sure their users—the content consumers—will stay on their platform.&lt;/p&gt;
&lt;h3 id=&quot;&quot;&gt;$$$&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Gee, Dmitry!! You are so anti-capitalism!&lt;/em&gt;
No I’m not.
I believe that people who produce content—should be able to get paid for their content, if they choose to do so.
I don’t mind pay-walls.
I just don’t want to pay to big corporations that will cut their chunk of the profit.
Supporting creators directly would be my preferred way.&lt;/p&gt;
&lt;p&gt;We already have a model where big corp gets the payment.
The music industry is a good example.
Small creators rarely see any real profits from their work, because labels take most of the money.
Do we really want to create yet another model like this?&lt;/p&gt;
&lt;h3 id=&quot;the-real-cost-of-outsourcing-your-content&quot;&gt;The real cost of outsourcing your content&lt;/h3&gt;
&lt;p&gt;Paying for the content, however, is not the biggest problem with these platforms.
The real cost comes in a simple fact—you no longer own your content.
It’s left to the mercy of the platform.&lt;/p&gt;
&lt;p&gt;I saw a LinkedIn post from &lt;a href=&quot;https://www.linkedin.com/in/gergelyorosz/&quot;&gt;Gergely Orosz&lt;/a&gt; recently.
In this post, Gergely said that his Google Document was blocked due to “&lt;em&gt;violations of the ToS&lt;/em&gt;”.
Gergely is a paying customer.
And even though he pays Google, Google didn’t bother to contact or notify Gergely that his document &lt;strong&gt;might&lt;/strong&gt; violate their ToS.
Here is the &lt;a href=&quot;https://www.linkedin.com/posts/gergelyorosz_googleworkspace-activity-7099737294235947008-W5Kb?utm_source=share&amp;#x26;utm_medium=member_desktop&quot;&gt;LinkedIn post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The internet is filled with examples of people who were blocked from their accounts, or whose work was removed from the platform.
YouTube, Medium, Spotify, Google—you can find examples of all of them.&lt;/p&gt;
&lt;p&gt;And what happens when a platform goes down?
Do you remember Revue?
It was a newsletter platform, which was owned by Twitter.
Shortly after Elon purchased Twitter, Revue stopped existing.
Luckily for me, who used this platform, and many others—Revue offered an option to take out the data.
Do they all offer such option?
Don’t know.&lt;/p&gt;
&lt;h3 id=&quot;you-are-your-brand&quot;&gt;You are your brand&lt;/h3&gt;
&lt;p&gt;It’s hard to talk, or in my case write, about branding without sounding like a wanna-be-twitter-influencer.
But in reality branding, and especially personal branding—existed for many years, decades even, if not centuries.
And for many content producers, their content becomes their brand.
I know of technical bloggers who are getting jobs because of their blogs.&lt;/p&gt;
&lt;p&gt;In his book, &lt;a href=&quot;https://www.amazon.com/Developer-Hegemony-Erik-Dietrich/dp/0692866809&quot;&gt;Developer Hegemony: The Future or Labor&lt;/a&gt; (not an affiliate link), Erik Dietrich talks a bit about branding.
One of the traits of the “opportunist developer”, as Erik calls them, is that they market themselves.
Here is what Erik writes about marketing and branding:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Everyone I interviewed markets themselves, with varying degrees of deliberateness. But all of them have made names for themselves. Blogging is, perhaps, the most common vehicle and was frequently mentioned. But podcast appearances, user group participation, video creation, and speaking at conferences all factor heavily into the equation for the developer opportunists. They have made themselves known by marketing themselves.&lt;/p&gt;&lt;p&gt;— Erik Dietrich, Developer Hegemony&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;But guess what happens to your brand when you let someone else own your content?
You are essentially creating a brand for the hosting platform.
When I land on a Medium article, I don’t care who wrote it.
I just learn that a lot of people write on Medium.&lt;/p&gt;
&lt;p&gt;But when I land on a personal blog, I explore a bit.
I check who is the author.
Where does he work?
What else did he write?
I subscribe to his newsletter; favorite his blog.&lt;/p&gt;
&lt;h2 id=&quot;how-to-own-your-content&quot;&gt;How to own your content&lt;/h2&gt;
&lt;p&gt;I hope I was able to convince you that owning your content, rather than renting it to a third-party platform, is the better alternative.
The next question you might ask is &lt;strong&gt;How&lt;/strong&gt;.
I can’t give you a definitive answer for every use-case.
But I can provide some guidelines, mainly for written content.&lt;/p&gt;
&lt;h3 id=&quot;host-it-yourself&quot;&gt;Host it yourself&lt;/h3&gt;
&lt;p&gt;The reason people resort to big name platforms—is because they might lack the technical know-how of hosting their own blog.
But since I posted this in my technical blog, rather than the &lt;a href=&quot;https://www.jikokaizen.blog/&quot;&gt;second one&lt;/a&gt;, I assume you are a technical person.
Hosting your own blog is not complicated.
Writing your own platform—takes a couple of evenings, and there are existing platforms like &lt;a href=&quot;https://gohugo.io/&quot;&gt;hugo&lt;/a&gt; or &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;.
And if you are really lazy, you can self-host WordPress.&lt;/p&gt;
&lt;p&gt;And it’s cheap.
Like $0 cheap.
You can host it on Netlify (not affiliated).
And if Netlify takes down your site, or it becomes expensive, you can move to any other place, &lt;strong&gt;because you own your content&lt;/strong&gt;.
By having your own domain, pointing to your own content, you will never lose SEO or branding due to movement from one hosting provider to another.
I honestly don’t understand why people choose existing platforms—that lock your readers in their ecosystem, and end up owning your content—rather than self-hosting.&lt;/p&gt;
&lt;h3 id=&quot;posse&quot;&gt;POSSE&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;POSSE?&lt;/em&gt;
&lt;em&gt;Yew, sounds gross!&lt;/em&gt;
But stay with me for a moment.
&lt;em&gt;&lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;POSSE&lt;/a&gt;&lt;/em&gt; is an abbreviation for &lt;strong&gt;Publish (on your) Own Site, Syndicate Elsewhere&lt;/strong&gt;.
It’s a common term in the IndieWeb movement with a simple mission: because you self-host your content, you shouldn’t deprive consumers from your knowledge.&lt;/p&gt;
&lt;p&gt;Our content is worth as long as it’s useable for our readers.
And, as the Stoics believe, I can only control my actions.
I can’t force everyone to go to my blog.
Some people do like the ease-of-use of existing platforms.
And they deserve to enjoy your knowledge as well.&lt;/p&gt;
&lt;p&gt;So the premise behind POSSE is to always publish on your own website—for the benefits of owning your content, and building your brand.
But syndicate this content to other platforms.
And remember to always use the correct &lt;code&gt;canonical&lt;/code&gt; URL, so search engines will prefer your self-hosted copy, over syndicated one.
And platforms that do not support &lt;code&gt;canonical&lt;/code&gt; URL—should cease from existing.&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;
&lt;p&gt;Owning your content concept extends way beyond blogs posts.
But this post is already at ~1700 words, and I want to make it short and to the point.
If you are interested in other forms of content ownership, and my approach to them—feel free to reach out to me via LinkedIn, Twitter, Mastodon, or Email.&lt;/p&gt;
&lt;h3 id=&quot;you-are-a-lair&quot;&gt;&lt;em&gt;”You are a lair!”&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Yes I am.
While I do believe in owning my content, I’m not without sins myself.
My &lt;a href=&quot;https://yieldnews.substack.com/&quot;&gt;newsletter&lt;/a&gt; is still powered by Substack.
However, I’m in the process of moving away from Substack.&lt;/p&gt;
&lt;p&gt;I’ll use those last words as shameless self-promotion.
I’m building a newsletter platform for the privacy centered people.
People who care about owning their content, rather than participating in the creation of yet another big-tech social-network.&lt;/p&gt;
&lt;p&gt;It’s currently under heavy development, and I share a lot of the process in my Twitter and Mastodon.
I encourage you to follow me there.
And if you don’t have any of them, then subscribe to my newsletter in order to be the first to know when it launches.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Own your content&lt;/em&gt;.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;That might not always be true. Some countries might forbid you from re-encoding the files &lt;strong&gt;you own&lt;/strong&gt;. Crazy, I know. IMNAL. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Hiking volcano Acatenango</title><link>https://kudmitry.com/articles/hiking-acatenango/</link><guid isPermaLink="true">https://kudmitry.com/articles/hiking-acatenango/</guid><pubDate>Fri, 25 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While &lt;a class=&quot;internal new&quot; href=&quot;/articles/digital-nomad-in-antigua/&quot;&gt;nomading in Antigua&lt;/a&gt;, I couldn’t help myself and find the most crazy adventure you can do there.
Hiking volcano Acatenango — was that adventure for me.&lt;/p&gt;
&lt;h2 id=&quot;acatenango&quot;&gt;Acatenango&lt;/h2&gt;
&lt;p&gt;Acatenango is a stratovolcano that is joined with Fuego volcano, and together they form a complex known as La Horqueta.
Acatenango last erupted in December 1972, and is now considered dormant.
Fuego, on the other hand, is active and throws ash and lava every 15–30 minutes.&lt;/p&gt;
&lt;p&gt;Hiking Acatenango requires 2 days.
Some people do overnight hike, when you start ascending at night, and descent in the morning.
But I did the regular overnight hike.&lt;/p&gt;
&lt;h2 id=&quot;finding-a-tour-operator&quot;&gt;Finding a tour operator&lt;/h2&gt;
&lt;p&gt;Like many activities in Guatemala, hiking Acatenango on your is dangerous.
Even though Acatenango is a national park, the hike goes through corn fields before you reach the entrance to the national park.
And it’s known that robberies occur on such roads.
So it’s always important to hike with an organized group.&lt;/p&gt;
&lt;p&gt;There are many tour operators located in Antigua.
I’m not going to cover all of them, but I’ll write a bit about the one I went with.&lt;/p&gt;
&lt;h3 id=&quot;wicho--charlies&quot;&gt;Wicho &amp;#x26; Charlie’s&lt;/h3&gt;
&lt;p&gt;W&amp;#x26;C is tour operator that does Acatenango hike.
They are located in Antigua, and they are very cool.&lt;/p&gt;
&lt;p&gt;Firstly, they have the best base camp location among all operators.
You sleep inside a hut, and not a tent, and the windows of the hut overlook Fuego.
This is very handy as you can stay in the comfort of your sleeping bag, while observing the eruptions from Fuego.&lt;/p&gt;
&lt;p&gt;Secondly, they provide gear rental.
You can rent everything from them, although I would not recommend renting clothes.
My wife and I, had all the clothes we needed, but we did rent backpacks and hiking poles.
They have a relatively good selection of gear, and I was happy to see Osprey backpacks available.&lt;/p&gt;
&lt;p&gt;Thirdly, they provide you with food.
Their food is vegan/vegetarian, which might be a bummer for some, but as a meat eater—I enjoyed their meals.
And you have to carry your food, which is a nice bonus for me since I came for the adventure, and the suffering.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: you can pay a porter to carry your backpack.&lt;/em&gt;
&lt;em&gt;I find it useless, as carrying my own gear is part of the challenge for me.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lastly, their guides are very experienced and speak English.&lt;/p&gt;
&lt;p&gt;I highly recommend you check them out at &lt;a href=&quot;https://wichoandcharlies.com/&quot;&gt;Whicho &amp;#x26; Charlie’s&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-first-day&quot;&gt;The first day&lt;/h2&gt;
&lt;p&gt;The day starts at W&amp;#x26;C office where you will have breakfast, rent the necessary gear, and have a briefing.
At around 9am, you will board a bus from Antigua, and will be taken to a small town called Aldea La Soledad.
The town is located at 2200 meters, and there you will start your ascent at around 10:30 am.&lt;/p&gt;
&lt;p&gt;The ascent takes between 4 and 6 hours, and &lt;strong&gt;is very steep&lt;/strong&gt;.
You will cross some corn fields, and after about 1 hour you will reach the entrance to the national park.
From there, the ascent will continue, and you will pass a cloud forest, and an arctic forest, before reaching the base camp at 3600 meters.&lt;/p&gt;
&lt;p&gt;Temperature will change drastically.
You will start, hopefully, during a sunny day, and will end up in a very cold environment.
So it’s very important to have at least 3 layers and a waterproof jacket.
During my hike we had rain, so the waterproof jacket was very helpful.&lt;/p&gt;
&lt;p&gt;After reaching the base camp, you will have two options: (1) rest and (2) ascent Fuego.&lt;/p&gt;
&lt;h3 id=&quot;option-1-ascending-fuego&quot;&gt;Option 1: Ascending Fuego&lt;/h3&gt;
&lt;p&gt;The ascent on Fuego starts at 4 pm, so you have to reach the base camp by that time.
Keep in mind that if until that point the hike was hard for you, Fuego ascend will be &lt;strong&gt;much harder&lt;/strong&gt;.
It’s a 4-hour hike (in addition to the 4 to 6 hours you already did), which goes down a bit, and then turns into a very, very steep ascend on gravel road.&lt;/p&gt;
&lt;p&gt;Neither me nor my wife did Fuego ascent.
When we arrived at the base camp, it was very cloudy with zero visibility of Fuego.
From the people in our group who did Fuego ascent, we understood that they saw nothing.&lt;/p&gt;
&lt;h3 id=&quot;option-2-rest-and-recharge&quot;&gt;Option 2: Rest and recharge&lt;/h3&gt;
&lt;p&gt;If you decide to skip Fuego, you will be given some time to rest and recharge.
You will be able to chill near a fireplace, and at 8 pm you will have dinner before going to sleep.
By that time, the group that decided to ascent Fuego, should be back already.&lt;/p&gt;
&lt;h2 id=&quot;day-2&quot;&gt;Day 2&lt;/h2&gt;
&lt;p&gt;As with the first day, here too, you have two options.&lt;/p&gt;
&lt;h3 id=&quot;option-1---be-ready-at-4-am-sharp&quot;&gt;Option 1 - be ready at 4 am sharp&lt;/h3&gt;
&lt;p&gt;The base camp is located at 3600 meters, while the summit of Acatenango is at 3976 meters.
After getting some sleep (which will be very minimal, trust me), you will have the option to be ready at 4 am for a summit sunrise hike.
The summit hike takes about 2 hours, of which you will spend about 20–30 minutes at the top, meeting the sunrise.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Acatenango base camp. Fuego on the background&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;650&quot; height=&quot;867&quot; src=&quot;/_astro/basecamp.Ubcr8Qpo_1G80RR.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Acatenango base camp. Fuego on the background&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Nobody from our group did that.
We set our alarms for 3:40 am, but woke up to the sound of intense rain hitting our metal roof.
So. Option 2.&lt;/p&gt;
&lt;h3 id=&quot;options-2---meet-the-sunrise-at-the-base-camp&quot;&gt;Options 2 - meet the sunrise at the base camp&lt;/h3&gt;
&lt;p&gt;The second option is to wake up whenever.
However, if you want to meet the sunrise, you need to be awake at around 5 am.
Which we did, and it was amazing!
All the rain ended, the clouds cleared, and we had an incredible opportunity to witness an unobstructed Fuego and a few eruptions.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Fuego on sunrise. The clouds cleared quickly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;650&quot; height=&quot;488&quot; src=&quot;/_astro/fuego.9Wg0c0tF_2fzYcp.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Fuego on sunrise. The clouds cleared quickly&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;It’s easily one of the most magic views I ever saw.
Being there, at 3600 meters, high above the clouds, near an active volcano.&lt;/p&gt;
&lt;p&gt;Then at around 7 am you will have breakfast, and start to descend.
The way down is much easier, and takes about 2 hours.
By around 10-11 am you are back at Aldea La Soledad, and by 12 am—1 pm you are back at W&amp;#x26;C office to return the rental gear and go rest.&lt;/p&gt;
&lt;h2 id=&quot;frequently-mentioned-questions&quot;&gt;Frequently mentioned questions&lt;/h2&gt;
&lt;p&gt;I’m a very organized person.
And I care deeply about my life and my health.
Therefore, before attempting (most of) crazy adventures in my life, I do a proper research.
This time was no different.
And so I want to address some common questions you might have, because from what I read online it’s the hardest thing people ever did, and you can die from altitude sickness.&lt;/p&gt;
&lt;h3 id=&quot;how-difficult-it-is&quot;&gt;How difficult it is?&lt;/h3&gt;
&lt;p&gt;As I said, some people online claimed this hike to be the hardest things they ever did.
I know that people like to exaggerate, but I rely on reviews and opinions online, so here is mine.&lt;/p&gt;
&lt;p&gt;Neither my wife nor I are in athletic shape.
My wife does a lot of yoga; I do gym and boxing.
Neither of us are runners.
We do like walking though.&lt;/p&gt;
&lt;p&gt;This hike was &lt;strong&gt;challenging&lt;/strong&gt;.
The ascent is constant, and you get about 15-20 minute section of flatness.
It’s a total of about 1600 meters of elevation gain.
Our calf muscles were hurting for the next 3 days.
But it’s far from the hardest thing I ever did in my life.&lt;/p&gt;
&lt;p&gt;It’s doable if you are somewhat active, and in shape.
I highly recommend you get hiking poles.
I usually oppose hiking poles, but during this hike they were very useful both on the way up, and the way down.
The soil is very soft, and it’s raining often, so additional points of contact are helpful.&lt;/p&gt;
&lt;p&gt;It’s also worth noting that none of us did either of the summits.
Maybe if you do the summit, it will be the hardest thing in your life.&lt;/p&gt;
&lt;h3 id=&quot;weather&quot;&gt;Weather&lt;/h3&gt;
&lt;p&gt;I want to say a few words about the weather.
From my understanding, August is the beginning of rainy season.
We had rain on the way up, and it was raining all night.
It also got very cold on the top.
I don’t know what temperature it was, but I suspect it dropped below zero Celsius.
Keep in mind that different seasons will have different weather.&lt;/p&gt;
&lt;h3 id=&quot;can-you-get-altitude-sickness&quot;&gt;Can you get altitude sickness?&lt;/h3&gt;
&lt;p&gt;I don’t know.
I’m not a doctor.
I used my Apple Watch to measure SpO2, and the lowest I got was 88% (everything below 95% is considered dangerous, according to internet experts).
Keep in mind that Apple Watch is not as accurate as a dedicated blood oxygen measuring device.&lt;/p&gt;
&lt;p&gt;Technically, you can get altitude sickness above 2500 meters.
It’s recommend that you stay in Antigua for a few days before attempting the hike, since Antigua is located at 1500 meters.
This can give you some time to get used to the altitude.&lt;/p&gt;
&lt;p&gt;The other thing that I know, is that when you summit Kilimanjaro—which is 5895 meters—the guides carry blood oxygen measuring devices, and supplemental oxygen.
Neither of this was carried by our guides, and so I assume altitude sickness is not that big of an issue on Acatenango.&lt;/p&gt;
&lt;p&gt;From our group only one girl had symptoms of dizziness on the way up.
But she hiked Pacaya (2550 meters) the day before, which is in my opinion—not a smart thing to do.&lt;/p&gt;
&lt;p&gt;If you have any concerns, consult with your doctor and the tour agency.
It’s also recommended to take a day of rest before the hike.
And stay hydrated.
The group is moving in a relatively slow pace, and the guides make sure everyone is doing okay.&lt;/p&gt;
&lt;p&gt;Happy hiking.&lt;/p&gt;</content:encoded></item><item><title>Digital Nomading in Antigua, Guatemala</title><link>https://kudmitry.com/articles/digital-nomad-in-antigua/</link><guid isPermaLink="true">https://kudmitry.com/articles/digital-nomad-in-antigua/</guid><pubDate>Thu, 24 Aug 2023 23:30:00 GMT</pubDate><content:encoded>&lt;p&gt;After leaving &lt;a class=&quot;internal new&quot; href=&quot;/articles/costa-rica-as-digital-nomad/&quot;&gt;Costa Rica&lt;/a&gt;, I arrived at my next destination — Guatemala.
But not the capital.
Instead, I went straight to Antigua Guatemala.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Antigua Guatemala and Agua Volcano&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;650&quot; height=&quot;867&quot; src=&quot;/_astro/antigua.BpZ5hsb0_Z1TYJju.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Antigua Guatemala and Agua Volcano&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The capital of Guatemala, Guatemala City, is considered dangerous, and many recommend you go straight to Antigua.
Antigua is a small city built in a Spanish colonial style.
It’s located about 1-hour drive from Guatemala City, and it’s a very vibrant city.
The moment I arrived, I fell in-love with.
Why?&lt;/p&gt;
&lt;h2 id=&quot;antigua-️&quot;&gt;Antigua ❤️&lt;/h2&gt;
&lt;h3 id=&quot;the-10-minute-city&quot;&gt;The 10-minute city&lt;/h3&gt;
&lt;p&gt;Firstly — it’s very walkable.
I really appreciate small and compact cities.
I call such cities: the 10-minute cities.
Why 10-minute?
Because everything you need is located 10-minute away by feet.
Gym, grocery store, restaurants, yoga studios.
All 10-minute away.&lt;/p&gt;
&lt;p&gt;In addition to that, Antigua is built in squares.
All streets cross each other at a 90-degree angle.
This means that navigating the city becomes very easy.
Third left, second right, and you got where you need.&lt;/p&gt;
&lt;p&gt;I really like this style of urban planning.&lt;/p&gt;
&lt;h3 id=&quot;nomad-friendly&quot;&gt;Nomad friendly&lt;/h3&gt;
&lt;p&gt;Secondly, Antigua is very nomad friendly.
It has a big community of digital nomads, and a good amount of cool places to work from (see &lt;a href=&quot;#recommendations&quot;&gt;#Recommendations&lt;/a&gt;).
And the coffee, oh the coffee!
It’s so great!&lt;/p&gt;
&lt;h3 id=&quot;the-scenery&quot;&gt;The scenery&lt;/h3&gt;
&lt;p&gt;Antigua is located between 4 volcanoes: Agua (my favorite), Pacaya, Acatenango and Fuego.
You walk in this small and compact city, and on the horizon you see these amazing volcanoes.
I don’t know about you, but living surrounded by volcanoes — is something special.&lt;/p&gt;
&lt;h2 id=&quot;travels&quot;&gt;Travels&lt;/h2&gt;
&lt;p&gt;However, my stay in Antigua wasn’t limited to the city itself.
Throughout the month I lived there, my wife and I traveled to some cool places.&lt;/p&gt;
&lt;h3 id=&quot;cerro-de-la-cruz&quot;&gt;Cerro de La Cruz&lt;/h3&gt;
&lt;p&gt;It’s a small viewing point, located about 25-minutes walk from the center.
From there you get an amazing view on Antigua as well as all the volcanoes.&lt;/p&gt;
&lt;h3 id=&quot;hiking-pacaya&quot;&gt;Hiking Pacaya&lt;/h3&gt;
&lt;p&gt;Pacaya is a volcano not far from Antigua.
It’s considered active, however there is no lava, and according to our guide it’s safe.
The hike itself is not hard, and you climb to around 2300m.
If you are in Antigua, I highly recommend you do this hike.&lt;/p&gt;
&lt;h3 id=&quot;hobbitenango&quot;&gt;Hobbitenango&lt;/h3&gt;
&lt;p&gt;Hobbitenango is a small resort built in the style of Lord of the Rings.
It’s located about 45 minutes drives from Antigua, and they have daily shuttles.
I’ve been to Hobbiton in New Zealand — which is the original shooting location of The Shire from LOTR — but I must admit I liked Hobbitenango way more.
It has more LOTR vibe, as it contains a restaurant, a brewery, and a lot of activities such as arrow shooting.
Also, the fact that you explore it on your own — and not with a guide as in Hobbiton — means you can spend more time there, and enjoy the scenery.
Highly recommended!&lt;/p&gt;
&lt;h3 id=&quot;hiking-acatenango&quot;&gt;Hiking Acatenango&lt;/h3&gt;
&lt;p&gt;There is a different post &lt;a class=&quot;internal new&quot; href=&quot;/articles/hiking-acatenango/&quot;&gt;Hiking Acatenango&lt;/a&gt; which you can check for the full experience, but in few words — I highly recommend you do it.
It’s a very challenging hike, but worth it.&lt;/p&gt;
&lt;h2 id=&quot;recommendations&quot;&gt;Recommendations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/99unCZVxfs4bzX3n8&quot;&gt;Artista de Cafe&lt;/a&gt; - My &lt;strong&gt;absolute favorite&lt;/strong&gt; coffee place and working location.
They have amazing coffee, and are usually filled with digital nomads.
A bit on the expensive side.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/ofvVwdAmZkm7rc6A9&quot;&gt;Serve Hope Cafe&lt;/a&gt; - A really cool café and working location.
They have a nice rooftop.
You can either work in the public café area, or rent a dedicated co-working table / meeting room.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/j93MNwKp1LACwsds8&quot;&gt;Union Cafe&lt;/a&gt; - A hipstery café for breakfast and/or lunch.
I also saw some people working from there.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/LC832tmibi8Vinne6&quot;&gt;La Bodegona&lt;/a&gt; - A huge supermarket with everything you need.
I usually bought all my groceries there.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/nLBZb6x6Vnseq58x5&quot;&gt;Orgánica&lt;/a&gt; - A small organic food store.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/9HuY3CCF95bK25Bh6&quot;&gt;Antiguas Gym&lt;/a&gt; - The gym I went to.
Very good, has all the equipment and free weights.
If you are into boxing, there is a Japanese guy named Cazu who does personal boxing sessions there.
Reach out to the gym and ask for Cazu.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/eUhXzvaFY3ujUUr39&quot;&gt;Shakti Shala Yoga Studio&lt;/a&gt; - If you are into yoga, my wife highly recommends this yoga studio.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I never heard about Antigua, but I’m glad I went there.
It’s easily one of my favorite cities, and I’ll definitely come back there.
The small buildings in Spanish style; the local population with their traditional outfits; the volcanoes — all this makes Antigua very special.&lt;/p&gt;
&lt;p&gt;Right now I’m on Lake Atitlan (I’ll post about it as well, maybe, so stay tuned), and will be back to Antigua for a few more days.
But I’m glad I stayed a month there.&lt;/p&gt;</content:encoded></item><item><title>Release Ratio</title><link>https://kudmitry.com/articles/release-ratio/</link><guid isPermaLink="true">https://kudmitry.com/articles/release-ratio/</guid><pubDate>Mon, 21 Aug 2023 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You know that feeling when you finish yet another self-development book, and all excited about your “new you”, but as days pass, nothing happens? Or worse, you get fatigued?&lt;/p&gt;
&lt;p&gt;I might have an answer for you. And no, it’s not a course, or a masterclass. Just sharing wisdom.&lt;/p&gt;
&lt;p&gt;A while back ago, I read an interesting blog post. The author was talking about the ratio of consumption and creation.&lt;/p&gt;
&lt;p&gt;Imagine that your brain is a bucket. And every time you consume, this bucket fills up. And when you consume too much, like a real bucket, it will explode or overflow.&lt;/p&gt;
&lt;p&gt;Consumption is mostly passive: reading books/articles/newsletters, watching YouTube/reels, listening to podcasts, attending self-help seminars, etc.&lt;/p&gt;
&lt;p&gt;And the only way to empty the bucket, is through a release valve called “creation”.&lt;/p&gt;
&lt;p&gt;Creation is active: writing a blog post, building a software, speaking at conferences, teaching, journaling, etc.&lt;/p&gt;
&lt;p&gt;Consumption is useless, unless you do some creation with it.&lt;/p&gt;
&lt;p&gt;Here is the blog post I was talking about &lt;a href=&quot;https://moretothat.com/release-ratio/&quot;&gt;https://moretothat.com/release-ratio/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Memento Mori</title><link>https://kudmitry.com/articles/memento-mori/</link><guid isPermaLink="true">https://kudmitry.com/articles/memento-mori/</guid><pubDate>Mon, 21 Aug 2023 02:40:00 GMT</pubDate><content:encoded>&lt;p&gt;In Latin, there is a phrase “memento mori”, which translates to “remember that you [have to] die”.
It’s used in many philosophical texts in Christianity as well as stoicism.&lt;/p&gt;
&lt;p&gt;The idea behind the phrase is the inevitability of death.
By reminding yourself, every day, that you will die — you start to appreciate life more.
And I see it in two lights.&lt;/p&gt;
&lt;p&gt;The first one, is that nothing matters.
Your problems today, your struggles — don’t matter, because eventually you are going to die.
And if you are going to die, then why bother worrying today?&lt;/p&gt;
&lt;p&gt;And the other light is that everything matters.
Because you are going to die, and you don’t know when — you need to appreciate what you have.
You need to be more brave and take more risks in order to live the life you want to live.
Because you might not have a chance to start tomorrow.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Since the day I’ve started this journey of indefinite travel, while building my own business, and working on my body and mind — I wanted to remember Memento Mori.
I was thinking to maybe get a tattoo with that phrase, but I’m 34, and I never had tattoos before.
I don’t think it’s the best age to get the first one.&lt;/p&gt;
&lt;p&gt;Then I was thinking about an amulet with a skull, which is a very common motif of memento mori.
But I already have an amulet, and I don’t want to get another one.&lt;/p&gt;
&lt;p&gt;Ring was another option.
Maybe with a writing or an image of skull.
But I don’t like rings, and the only ring I wear is my wedding ring.&lt;/p&gt;
&lt;p&gt;Bracelet became the favorite choice.
However, it was hard to find a bracelet with this phrase.
Memento mori is not that popular in Central America.&lt;/p&gt;
&lt;p&gt;But today I finally found it.
I found a local merchant, decedent of Maya.
He made me this bracelet, from scratch.
In front of my eyes.
It was fascinating.&lt;/p&gt;
&lt;p&gt;I now wear it as constant reminder — &lt;strong&gt;Memento Mori&lt;/strong&gt;.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Memento Mori bracelet&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;1440&quot; src=&quot;/_astro/memento_mori.Ugor4ERR_kYBV8.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Memento Mori bracelet&lt;/figcaption&gt;&lt;/figure&gt;</content:encoded></item><item><title>Apple watch, a curse or a blessing?</title><link>https://kudmitry.com/articles/apple-watch-a-curse-or-a-blessing/</link><guid isPermaLink="true">https://kudmitry.com/articles/apple-watch-a-curse-or-a-blessing/</guid><pubDate>Sat, 19 Aug 2023 22:45:00 GMT</pubDate><content:encoded>&lt;p&gt;From my observations, the pandemic has been one of the best times for Apple as a company.
Everyone around me switched to Apple.
I also did the switch and got myself an iPhone and Apple Watch.&lt;/p&gt;
&lt;p&gt;The functionality that Apple Watch provides, is enormous.
For starters, I never missed a meeting with the Apple Watch.
A glance at the time, would also provide me with my next meeting.
Apart from that, the ability to RSVP to meetings, approve two-factor authentications, and monitor my heart rate - were very comfortable additions.&lt;/p&gt;
&lt;p&gt;However, as time passed, I lost enthusiasm toward Apple Watch.
Instead of a productivity tool, I started to feel like it’s an electronic handcuff.
I’d wear it first thing in the morning, and it was the last thing in the evening I’d remove from my body before sleeping.
The constant buzzing and vibrations, also drove me nuts.&lt;/p&gt;
&lt;p&gt;And so, when I quit my job, the first thing I did - was to stop wearing Apple Watch.
I no longer have meetings that I can miss, and honestly, everything else I can do on my phone anyway.&lt;/p&gt;
&lt;p&gt;Today, after few days that it was lying around, I powered it off, and tucked away in one of my electronics organizer bags.
And I really don’t know what to do with it anymore.&lt;/p&gt;</content:encoded></item><item><title>On time management</title><link>https://kudmitry.com/articles/on-time-management/</link><guid isPermaLink="true">https://kudmitry.com/articles/on-time-management/</guid><pubDate>Wed, 09 Aug 2023 23:40:00 GMT</pubDate><content:encoded>&lt;p&gt;During my coaching session today, I’ve been asked about time-management.
It’s not directly related to software engineers, but time management is something that many people struggle with.&lt;/p&gt;
&lt;p&gt;I believe that one important aspect for healthy time-management—is having a routine.
Try to wake up, and go to sleep at the same time.
And try to have rituals.
My morning ritual includes a cup of coffee, and then hitting the gym or having a boxing session.
My night ritual includes reading before I go to sleep.&lt;/p&gt;
&lt;p&gt;Another aspect—is time blocking.
I live inside my calendar, even today when I don’t have a proper job.
My calendar allows me to time-block different activities such as: gym, travel plans, and work on my business.&lt;/p&gt;
&lt;p&gt;Lastly, if you feel like you waste your days, I recommend you try journaling.
Journaling is a way to reflect on a day that passed, and give you credit for things you’ve achieved, even if your brain feels like you did nothing.
If, however, it turns out you did less than expected—it allows you to plan your next day better.&lt;/p&gt;
&lt;p&gt;By incorporating these three aspects, you can improve your time-management skills.&lt;/p&gt;</content:encoded></item><item><title>Evergoods CHZ 22L - A Review</title><link>https://kudmitry.com/articles/evergoods-chz-22l-a-review/</link><guid isPermaLink="true">https://kudmitry.com/articles/evergoods-chz-22l-a-review/</guid><pubDate>Wed, 09 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I purchased the Evergoods CHZ 22L about a few months ago, intending to use it as my daily all-around backpack.
Here is my review of it.&lt;/p&gt;
&lt;p&gt;As I mentioned in my previous review, of the &lt;a class=&quot;internal new&quot; href=&quot;/articles/minaal-carry-on-3-0-review/&quot;&gt;Minaal Carry-on&lt;/a&gt; backpack: I don’t have access to good backpacks.
I’m left at the mercy of paying big taxes and shipping fees, and hope to like the product.
With the Evergoods CHZ, I must admit, I’m disappointed.&lt;/p&gt;
&lt;h2 id=&quot;intended-use&quot;&gt;Intended use&lt;/h2&gt;
&lt;p&gt;As a preparation for my nomading in Central America (read about my first destination &lt;a class=&quot;internal new&quot; href=&quot;/articles/costa-rica-as-digital-nomad/&quot;&gt;Costa Rica&lt;/a&gt;, I decided to get an all-around backpack.
A backpack that will be good for carrying a laptop to a co-working space; one that is good to go to the gym with; one I can hike with.
Oh, and I wanted one that is relatively packable, as during flight, I carry the &lt;a class=&quot;internal new&quot; href=&quot;/articles/minaal-carry-on-3-0-review/&quot;&gt;Minaal Carry-on&lt;/a&gt;, and pack this backpack inside my checked luggage.&lt;/p&gt;
&lt;h2 id=&quot;what-i-like&quot;&gt;What I like&lt;/h2&gt;
&lt;p&gt;I like the fact that the Evergoods CHZ is a good, all-around backpack.
It’s roomy enough for the use-cases I’ve mentioned above.
I appreciate the fact that it has two water bottle sleeves, which means I can throw in a snack when hiking, or a tripod when doing some insta content.&lt;/p&gt;
&lt;p&gt;It also feels well-built, and I’m not afraid to take it to hikes with me.
Evergoods claim that it’s water-repellent, but I didn’t have the chance to test it.
Under light rain, it performed well, and kept my stuff dry.
And its neutral look helps it fit in any environment.&lt;/p&gt;
&lt;h2 id=&quot;what-i-dont-like&quot;&gt;What I don’t like&lt;/h2&gt;
&lt;p&gt;Unfortunately, the list of dislikes is bigger.&lt;/p&gt;
&lt;p&gt;I really dislike the shoulder straps.
They are very tough, and painful on the shoulders.
I read other people saying that they break in, but I was wearing it daily for more than 4 months, and mine are still tough.&lt;/p&gt;
&lt;p&gt;Next, the laptop sleeve.
The Minaal is very big for day-to-day use, so I was looking at a backpack that is good to travel daily with a laptop.
I knew before that the laptop sleeve in the CHZ is not awesome.
But I didn’t expect it to be that disappointing.
It’s very hard to get the laptop in and out, when the small pocket is full.
And I usually carry my glasses/sunglasses in the top pocket, so it’s always full.&lt;/p&gt;
&lt;p&gt;The laptop sleeve fits both 16” and 14” MacBook Pro, but as I said, I’m disappointed with sleeve.
In my next backpack, I’ll be looking for a dedicated laptop compartment, like in the Minaal.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall, I find the Evergoods to be too painful for my needs.
During long day hikes, when I carry a lot of water and clothes, I tend to develop shoulder pain.&lt;/p&gt;
&lt;p&gt;As for the laptop compartment, I think that the Evergoods CPL has a better design, since it has a dedicated laptop compartment.
However, if it has the same straps as the CHZ then I’m not sure either of them would have been a good choice for me.&lt;/p&gt;</content:encoded></item><item><title>Short Review - Minaal Carry-on 3.0</title><link>https://kudmitry.com/articles/minaal-carry-on-3-0-review/</link><guid isPermaLink="true">https://kudmitry.com/articles/minaal-carry-on-3-0-review/</guid><pubDate>Tue, 08 Aug 2023 23:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One year ago, I purchased the Minaal Carry-on 3.0 as my carry-on backpack.
Here is my short review of it.&lt;/p&gt;
&lt;h2 id=&quot;intended-use&quot;&gt;Intended use&lt;/h2&gt;
&lt;p&gt;I purchased the backpack in order to replace the traditional carry-on trolley.
I find backpacks to be nicer for navigation around the airport, as they allow you to have your hands free.
I was looking for a spacious backpack that can house some clothes, as well as electronics such as laptop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please note:&lt;/strong&gt; I don’t one-bag.
In most of the cases, I take a standard suitcase with me, in addition to a carry-on.
So this review is not from a point-of-view of someone who is one-bagging.&lt;/p&gt;
&lt;h2 id=&quot;where-and-how-i-used-it&quot;&gt;Where and how I used it&lt;/h2&gt;
&lt;p&gt;I used the Minaal Carry-on on two different occasions.
The first time I use it, was when I was nomading in The Netherlands (you can read about it &lt;a class=&quot;internal new&quot; href=&quot;/articles/remote-work-in-the-netherlands-part-1/&quot;&gt;here&lt;/a&gt;, and &lt;a class=&quot;internal new&quot; href=&quot;/articles/remote-work-in-the-netherlands-part-2/&quot;&gt;here&lt;/a&gt;.
The second time—is right now.
I use the backpack while nomading in Central America (you can read about my first destination &lt;a class=&quot;internal new&quot; href=&quot;/articles/costa-rica-as-digital-nomad/&quot;&gt;Costa Rica&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-i-like-about-it&quot;&gt;What I like about it&lt;/h2&gt;
&lt;p&gt;I must admit, I’m in &lt;strong&gt;love&lt;/strong&gt; with this backpack.
It’s super comfortable, and relatively roomy (more on that later).&lt;/p&gt;
&lt;p&gt;I like the fact that it has a dedicated laptop sleeve.
It really helps with airport security, as you don’t have to empty, and repack it, every time you want to take the laptop out.
The laptop sleeve has a unique adjustable design, and I was able to fit a 16” MacBook Pro, and a 14” MacBook Pro, and it felt very secure in there.
Moreover, the sleeve can house small electronics such as Bluetooth keyboard, external hard drive, laptop stand, etc.
I call it: the electronics compartment.
I also appreciate the secret back pocket, where you can store cash and passport without worrying that people behind you might steal it.&lt;/p&gt;
&lt;p&gt;The backpack and the straps are very comfortable.
It’s among one of the most comfortable backpacks I ever used.
I also appreciate that you can clip the sternum strap, so it won’t dangle when you walk.&lt;/p&gt;
&lt;h2 id=&quot;what-i-dont-like-about-it&quot;&gt;What I don’t like about it&lt;/h2&gt;
&lt;p&gt;First, the size.
It is standard carry-on size, but I have a feeling that it’s smaller than say an American Tourister carry-on trolley.
It’s not a big deal for me, and I assume Minaal wanted to fit as many airline guidelines, as possible.
I have never been asked by airport stuff to measure the backpack.&lt;/p&gt;
&lt;p&gt;The main compartment has some organization pockets, but I rarely use them.
Once again, my use case is not one-bagging, so I usually carry my electronics and some clothes inside the backpack.
If you one-bag, you might find the organization pockets useful.&lt;/p&gt;
&lt;p&gt;I like the water bottle sleeve, but I really wish it had two of them.
This would have been useful for carrying a tripod, for example.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I, usually don’t have access to trying backpacks before purchase.
My only approach is pay a lot for shipping, and hope I like it.
In the case with Minaal — &lt;strong&gt;I love it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I find it to be a prefect travel backpack, and very comfortable.
I would also recommend you to get the hip-belt, as it might get a bit heavy (I like to suffer, so I don’t take the backpack off my back while standing in lines).&lt;/p&gt;
&lt;p&gt;And when storing it, you can remove the hip-belt completely, and stow away the shoulder straps so, it becomes like a small suitcase.
In summary, I’m very happy I got that backpack.&lt;/p&gt;</content:encoded></item><item><title>Costa Rica as Digital Nomad</title><link>https://kudmitry.com/articles/costa-rica-as-digital-nomad/</link><guid isPermaLink="true">https://kudmitry.com/articles/costa-rica-as-digital-nomad/</guid><pubDate>Sat, 29 Jul 2023 01:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As some of you might know, &lt;a class=&quot;internal new&quot; href=&quot;/articles/an-essay-on-burnout/&quot;&gt;I&apos;ve burnt out&lt;/a&gt;.
My wife and I took our dog, and bought one way ticket to Costa Rica.
The plan?
Slow travel, combined with building a sustainable business, and working on my mind, and body.
Why Costa Rica?
Don’t know.
I think we discussed it as possible next travel destination, before the decision to nomad.&lt;/p&gt;
&lt;h2 id=&quot;finding-accommodation-and-settling-in-san-josé&quot;&gt;Finding accommodation, and settling in San José&lt;/h2&gt;
&lt;p&gt;Finding accommodation was tricky.
We wanted a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Safe and comfortable house&lt;/li&gt;
&lt;li&gt;A good place for day-to-day life: good kitchen, washing machine&lt;/li&gt;
&lt;li&gt;Preferably a place with comfortable working environment&lt;/li&gt;
&lt;li&gt;Dog friendly&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most of the accommodations outside the capital, were, how to say it…
Not suitable for long term (at least a month) rental.
I don’t know how to describe these accommodations, but I felt that I couldn’t stay there for long period, and we didn’t want to move every week.
Finding a pet friendly place was also a challenge that reduced the amount of options drastically.
Lastly, budget might have played a role.
So YMMV.&lt;/p&gt;
&lt;p&gt;And so, after a brief discussion, we changed plan.
Stay in a modern apartment in San José, and travel from there.
I know that many people would not recommend staying in the capital, but considering our options, this was the best decision.&lt;/p&gt;
&lt;p&gt;We found a perfect apartment in Guachipelin neighborhood.
It had an amazing kitchen, very safe and secure.
A co-working space was adjacent to the condominium.
All looked good, except for one fact — it was a gated community.&lt;/p&gt;
&lt;h3 id=&quot;living-like-the-rich&quot;&gt;Living like the rich&lt;/h3&gt;
&lt;p&gt;The first thing that our taxi driver told us upon arriving at our Airbnb is: “Ohh, very nice area. Rich people live here.”
We’ve heard this phrase multiple times from Uber drivers throughout our trip.
And it did feel rich.
Secure condominiums, like small gated communities.
We’ve never been in Central, or South, America, so this sense of security was important to us.&lt;/p&gt;
&lt;p&gt;But with this sense of security, also came a sense of inability.
There was nowhere to go.
We’ve mainly ordered groceries through Uber Eats.
Going to our activities, or the city center, required an Uber.
I guess it’s fine if you rent a car, but we didn’t.
And so we felt like we were stuck there, and were very dependent on Uber.&lt;/p&gt;
&lt;h3 id=&quot;battling-the-jet-lag&quot;&gt;Battling the Jet Lag&lt;/h3&gt;
&lt;p&gt;The first 4–5 days were tough.
I’ve experienced the biggest jet-lag ever: 9 hours.
I would go to sleep at midday; wake-up in the middle of the night.
We did try to go and explore the city, but found it to be boring, and unattractive.
And I barely did any work during that period.&lt;/p&gt;
&lt;h3 id=&quot;mini-vacations&quot;&gt;Mini-vacations&lt;/h3&gt;
&lt;p&gt;The idea was to live a “normal” life: work on my business, cook at home, do boxing, but also take mini-vacations once in a while.
The first mini-vacation we took was the Poás Volcano.
It’s a day trip from the capital, and you get to see the most acidic lake in the world.
I saw many volcanoes in my life (hello Iceland, and New-Zealand), so it’s hard to impress me, but Poás is a very nice volcano, due to the fact it has an acidic lake in the crater.
It’s also considered active, with last eruption in September 2019.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Poás Acidic Lake&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;6000&quot; height=&quot;4000&quot; src=&quot;/_astro/poas-acidic-lake.BKkavs1p_Z1zKHp3.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Poás Acidic Lake&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The second mini-vacation was longer.
We’ve rented a car and went around to La Fortuna, and Monteverde.
In La Fortune we visited the hot springs, which is a must-do.
Night walk was also amazing, you see different animals and inspects at night.
Hiking the Arenal Volcano was also a good reconnection with nature.
The weather in La Fortuna was hotter than in central valley.&lt;/p&gt;
&lt;p&gt;Monteverde, on the other side, was colder in terms of weather.
We hiked the cloud forest, which is a unique experience.
And also did the zip-line, which I recommend (especially the Tarzan swing, if you like adrenaline).
I’ve heard that the city itself is also very nice, but we didn’t really have time to experience it, and mostly enjoyed the nature around the city, and the hot tub in Selina Hotel.&lt;/p&gt;
&lt;p&gt;The third and final mini-vacation, was a day trip from the capital to a coffee plantation which was organized by Britt — a local coffee producer.
I really like coffee, and I enjoyed this tour very much.
If you are a coffee lover, I highly recommend it.&lt;/p&gt;
&lt;p&gt;After one month in San José, we left it, and moved to a different region.&lt;/p&gt;
&lt;h2 id=&quot;nosara---a-heaven-on-earth&quot;&gt;Nosara - a “heaven” on earth&lt;/h2&gt;
&lt;p&gt;I’m always skeptical when I hear the phrases “heaven on earth”, or “paradise on earth”.
The last time I was in such “heaven”, was on one of the Gili Islands in Indonesia.
It was a bizarre experience: luxury accommodations for tourists, alongside destroyed local community who live in complete poverty.
If this is heaven, I’m not sure if I want to get there after death.&lt;/p&gt;
&lt;p&gt;Anyway, Nosara.
Considered a blue zone, and a very popular destination among surfers, people who look for spirituality, and digital nomads.
In three words: I disliked it.
It’s completely unwalkable, and when it rains (which is very often), it’s covered in mud.
The main transportation is ATV or a motorbike.
It’s extremely overpriced for what it offers.
It clearly caters to “rich” Americans, as the prices are in US Dollars rather than Costa Rican Colóns.
Our first accommodation was pretty far from the center, however the second one was closer to the main restaurants and activities.
I didn’t find many opportunities to work from cafés, and worked mainly from my Airbnb.&lt;/p&gt;
&lt;p&gt;The drive there was also hard.
It took us 5 hours to get to Nosara, and around 7 hours to get back to San José due to car accident on the road, and insane traffic.
Unless you are surfing, or like yoga (I’ve heard it a good place for yoga), I’d avoid Nosara.&lt;/p&gt;
&lt;h2 id=&quot;back-to-san-josé&quot;&gt;Back to San José&lt;/h2&gt;
&lt;p&gt;After two weeks in Nosara, we were back to San José for our final days, before leaving Costa Rica, and flying to our next destination.
This time we rented an Airbnb in La Sabana area, which is a nice, central area.
The condo was smaller than the one in Guachipelin, but it’s fine, as the main purpose of those days was to unwind, relax, and prepare to our next destination.
We also took a one-day trip to Manuel Antonio national park.
It’s nice park, you can see some animals, but the road is though.
You wake up at 5 am, and back by 6-8 pm, depending on the traffic.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;How would I sum up Costa Rica?
Bear in mind that I’m judging it from a perspective of a digital nomad, a very fresh digital nomad.
I don’t regret going there.
I think it was a good decision as a first Central American country, but I didn’t find anything memorable from this trip.
Yes the nature is nice.
The weather is comfortable, but mostly in the capital.
We felt safe there most of the time.&lt;/p&gt;
&lt;p&gt;I didn’t find many places to work from.
Maybe it’s because I stayed mainly in the capital.
Selina Hotel in Monteverde was nice, and I think there is a Selina in La Fortuna, and one in Nosara.
So maybe if you do Selina hopping your experience will be different.
My wife and I, were focusing more on comfortable day-to-day living.&lt;/p&gt;
&lt;p&gt;Lastly, Costa Rica is expensive.
Very expensive.
Some places would be close to Iceland prices, and Iceland is one of the most expensive countries I’ve travelled to.&lt;/p&gt;
&lt;p&gt;So unless you combine your nomading with activities such as surfing, or yoga; or you are comfortable with Selina, or travel without a dog — I’d skip Costa Rica.&lt;/p&gt;
&lt;h2 id=&quot;general-recommendations&quot;&gt;General recommendations&lt;/h2&gt;
&lt;h3 id=&quot;internet&quot;&gt;Internet&lt;/h3&gt;
&lt;p&gt;I found the internet in Costa Rica to be very reliable.
No matter where we’ve been, the internet was stable.
One exception was Nosara, where they had occasional power outages, which according to our Airbnb hosts where caused by the massive construction on the Peninsula.
The power would immediately come back though.&lt;/p&gt;
&lt;h3 id=&quot;car-rental&quot;&gt;Car rental&lt;/h3&gt;
&lt;p&gt;I’ve read a lot of scary stories online, ranging from “bad roads”, all the way to “cops are targeting tourists”, and “rental companies are scamming tourists”.&lt;/p&gt;
&lt;p&gt;We’ve rented a car 3 times, and had a good experience.
The roads are bad once you get outside the central valley, but are tolerable.
I recommend getting an SUV, as opposed to a small car, or sedan.
You will appreciate the higher clearance.
The price difference is a couple of dollars anyway.&lt;/p&gt;
&lt;p&gt;For our travel in La Fortuna, and Monteverde we used &lt;a href=&quot;https://www.adobecar.com/&quot;&gt;Adobe&lt;/a&gt;.
The process was smooth, and painless.&lt;/p&gt;
&lt;p&gt;For our trip to Nosara, and back, Adobe was not available as they don’t have an office in Nosara.
To Nosara we used &lt;a href=&quot;https://www.alamo.com/en/home.html&quot;&gt;Alamo&lt;/a&gt;, however on the way back Alamo was not available (no idea why), so we used &lt;a href=&quot;https://www.nationalcar.com/en/home.html&quot;&gt;National&lt;/a&gt; which to my understanding is exactly the same as Alamo — they are located in the same office, and are handled by the same staff.
Again, the process was smooth and easy.&lt;/p&gt;
&lt;h3 id=&quot;co-working-and-cafés&quot;&gt;Co-working and cafés&lt;/h3&gt;
&lt;p&gt;As I briefly mentioned, I didn’t have much opportunity to work from other places.
Most of the work was done in the Airbnb, and in case of the first Airbnb, in the adjacent co-working space.&lt;/p&gt;
&lt;p&gt;I can’t give recommendations for San José, but if you do find yourself in Nosara, here are some good places to work from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/BeLFhibpjqNCxcnY8&quot;&gt;Sunrise Coffee&lt;/a&gt; — A nice cafe, with tasty food. Saw some nomads there.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/d6xbgiZ9QowMbCa57&quot;&gt;Bodhi Tree Yoga Resort&lt;/a&gt; — A big resort, with many activities, which include a juice bar where you can eat, and work.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/dMoMHsS42xSTm7wL6&quot;&gt;Norte Hotel&lt;/a&gt; — I worked from there, sitting by the pool.
Cool vibes, and a nice bar.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://goo.gl/maps/g82xxisjf3oJS9V98&quot;&gt;Roota Mercado&lt;/a&gt; — An organic grocery store, that also sells food, and drinks.
Saw some nomads working from there.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;general-recommendations-1&quot;&gt;General recommendations&lt;/h3&gt;
&lt;p&gt;Some general recommendations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you are into boxing, I &lt;strong&gt;highly&lt;/strong&gt; recommend &lt;a href=&quot;https://goo.gl/maps/tQD6vsZmw49M2pU79&quot;&gt;Striking Performance Center&lt;/a&gt;.
It’s a great martial arts school, and Rafael — who is the owner — is an awesome coach.
I really enjoyed training with him.&lt;/li&gt;
&lt;li&gt;If you want to stay fit, and find yourself in Nosara — I can recommend &lt;a href=&quot;https://goo.gl/maps/gGZDbDq38f1Fbner9&quot;&gt;Bodhi Tree Community Gym&lt;/a&gt;.
There are two gyms at the Bodhi Resort.
One is a fancy-shmancy, hotel-like gym, and the second is the community gym.
I found the community gym to be better equipped, and it is cheaper (still expensive, because it’s Nosara).&lt;/li&gt;
&lt;li&gt;If you are into yoga, my wife says that the teachers at &lt;a href=&quot;https://goo.gl/maps/d6xbgiZ9QowMbCa57&quot;&gt;Bodhi Tree Yoga Resort&lt;/a&gt;, are very good.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;We left Costa Rica almost two weeks ago, and are now in Guatemala.
Stay tuned for trip report of Guatemala, I feel it’s going to be more interesting than Costa Rica.
Happy nomading!&lt;/p&gt;</content:encoded></item><item><title>How to auto-generate OpenGraph images</title><link>https://kudmitry.com/articles/how-to-auto-generate-og-images/</link><guid isPermaLink="true">https://kudmitry.com/articles/how-to-auto-generate-og-images/</guid><pubDate>Fri, 28 Jul 2023 01:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ever seen those nice image previews when you share a link on Twitter, LinkedIn, Mastodon, etc? Those are called OpenGraph images, more on that in a minute. Creating those images, is however, time-consuming. Especially if you are focused on writing. I used to use Unsplash images, which are free, up until the moment I shared this very important &lt;a href=&quot;https://github.com/whatwg/html/issues/8693&quot;&gt;issue&lt;/a&gt;, that I’ve opened on GitHub, to my LinkedIn followers.&lt;/p&gt;
&lt;p&gt;The image you see when you share any GitHub issue—is auto generated, and contains the information about the issue itself. Ain’t this cool? We can do the same!&lt;/p&gt;
&lt;h2 id=&quot;what-is-opengraph&quot;&gt;What is OpenGraph?&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;OpenGraph is a protocol that enables any web page to become a rich object in a social graph - &lt;a href=&quot;https://ogp.me/&quot;&gt;https://ogp.me/&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The idea was created, and popularized by Facebook, but in a nutshell—it’s a bunch of &lt;code&gt;meta&lt;/code&gt; tags, that different platforms such as Twitter, or LinkedIn—can read, and generate preview cards based on them. They were initially made to index the content inside a graph, but we care only about the preview card.&lt;/p&gt;
&lt;p&gt;You can visit the official website to learn more about all the meta tags. We, however, will focus only on one: &lt;code&gt;og:image&lt;/code&gt;. And because back in the days when Facebook was popular, Twitter tried to compete with it, it’s no surprise that Twitter has its own set of meta tags. For our use case, we will focus on &lt;code&gt;twitter:image&lt;/code&gt; meta tag. You can read more about Twitter tags &lt;a href=&quot;https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup&quot;&gt;here&lt;/a&gt;. Also keep in mind that Twitter can parse OpenGraph tags, in case Twitter tags are not present.&lt;/p&gt;
&lt;h2 id=&quot;how-to-auto-generate-the-image&quot;&gt;How to auto-generate the image&lt;/h2&gt;
&lt;p&gt;Meet &lt;a href=&quot;https://github.com/vercel/satori&quot;&gt;&lt;code&gt;satori&lt;/code&gt;&lt;/a&gt;. Satori is an extremely cool library that converts HTML and CSS into SVG images. One thing to keep in mind is that &lt;code&gt;satori&lt;/code&gt; supports a sub-set of the CSS properties, so you can’t go too wild. Refer to &lt;a href=&quot;https://github.com/vercel/satori#css&quot;&gt;this table&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;satori&lt;/code&gt; also supports JSX, but I don’t see a reason to bring an entire Frontend framework, just to render one image. Instead, we will use &lt;a href=&quot;https://github.com/natemoo-re/satori-html&quot;&gt;&lt;code&gt;satori-html&lt;/code&gt;&lt;/a&gt; which is an HTML adapter this will allow us to write plain HTML.&lt;/p&gt;
&lt;p&gt;First, install the libraries:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; satori&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; satori-html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As an example, we will use my website’s OpenGraph image which can be found &lt;a href=&quot;https://www.yieldcode.blog/og.png&quot;&gt;here&lt;/a&gt;. I’ll spit the code first, and then we will discuss it.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; markup&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &amp;#x3C;div&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    style=&quot;width: 1200px; height: 630px; display: flex; flex-direction: column; flex-wrap: wrap; justify-content: center; align-items: center; padding: 0 24px;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &amp;#x3C;img&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      src=&quot;https://www.yieldcode.blog/images/cover.jpg&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      width=&quot;500&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      height=&quot;500&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      style=&quot;width: 150px; height: 150px; border: 1px solid white; border-radius: 50%;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &amp;#x3C;h1 style=&quot;color: rgb(17,24,39); font-size: 5.5rem; font-weight: 700&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SITE_AUTHOR&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;#x3C;/h1&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &amp;#x3C;h2 style=&quot;color: rgb(82,82,82); font-size: 2.5rem; font-weight: 700&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SITE_AUTHOR_TITLE&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;#x3C;/h2&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &amp;#x3C;h3 style=&quot;color: rgb(82,82,82); font-size: 1.5rem; font-weight: 400&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SITE_DESCRIPTION&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;#x3C;/h3&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; svg&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; satori&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;markup&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1200&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;630&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  embedFont&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  fonts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Inter&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;interRegularData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      weight&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      style&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;normal&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Inter&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;interBoldData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      weight&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;700&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      style&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;normal&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing we notice straight away, is that &lt;code&gt;satori&lt;/code&gt; does not support classes, and instead requires the usage of &lt;code&gt;style&lt;/code&gt; attributes.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;html`&lt;/code&gt; thingy comes from &lt;code&gt;satori-html&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;satori-html&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;handling-fonts&quot;&gt;Handling fonts&lt;/h3&gt;
&lt;p&gt;The other thing we notice—is how fonts are handled. Since we do not render complete HTML page, fonts are not handled through the &lt;code&gt;head&lt;/code&gt; tags. Instead, we need to load the fonts manually, and feed them to &lt;code&gt;satori&lt;/code&gt;. In my case I use the &lt;code&gt;Inter&lt;/code&gt; font in its regular and bold variations. This means that I need to get the font files and read them.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fs&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;fs/promises&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;interRegularData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;interBoldData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;readFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;./src/assets/fonts/inter/Inter-Regular.woff&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;readFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;./src/assets/fonts/inter/Inter-Bold.woff&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;to-svg-or-not-to-svg&quot;&gt;To SVG or not to SVG?&lt;/h3&gt;
&lt;p&gt;We could stop there, but Twitter being Twitter:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;URL of image to use in the card. Images must be less than 5MB in size. JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an animated GIF will be used. SVG is not supported.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So for the sake of compatibility, it would be best to convert the SVG to PNG. Meet &lt;a href=&quot;https://github.com/lovell/sharp&quot;&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/a&gt;—a high performance Node.js image processing library. Sharp can do a lot of things such as: converting one type to another, resize, and rotate images.&lt;/p&gt;
&lt;p&gt;The conversion is straight forward:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; png&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; sharp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Buffer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;png&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toBuffer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it. What’s left now, is sending the &lt;code&gt;png&lt;/code&gt; as a body of the response, together with the correct header: &lt;code&gt;Content-Type: image/png&lt;/code&gt;. If you are familiar with express-like libraries, this should be easy for you.&lt;/p&gt;
&lt;h2 id=&quot;bonus---astro&quot;&gt;Bonus - Astro&lt;/h2&gt;
&lt;p&gt;I &amp;#x3C;3 Astro. I think it’s a great framework to build static websites. And it’s not a surprise that all my websites, including this blog, are powered by Astro. It can be a bit tricky to add this functionality to Astro, so I decided to dedicate a section for this.&lt;/p&gt;
&lt;p&gt;Astro has a notion of &lt;code&gt;APIRoute&lt;/code&gt;. &lt;code&gt;APIRoute&lt;/code&gt; is a away to generate non HTML pages, such as: RSS feeds, sitemaps, and images. Following our example image, we would like to create a new file, inside our &lt;code&gt;pages&lt;/code&gt; folder, named &lt;code&gt;og.png.ts&lt;/code&gt;. The content of this file is very simple:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;APIRoute&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;astro&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;APIRoute&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // read fonts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // created markup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // generate svg with satori&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // convert svg to png with sharp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;png&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    status&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    headers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;      &quot;Content-Type&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;image/png&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it. Upon building your static website, Astro will also generate a file named &lt;code&gt;og.png&lt;/code&gt; which will be the image that &lt;code&gt;satori&lt;/code&gt; generated from your HTML markup, and &lt;code&gt;sharp&lt;/code&gt; converted to PNG.&lt;/p&gt;
&lt;h3 id=&quot;bonus-2&quot;&gt;Bonus 2&lt;/h3&gt;
&lt;p&gt;It’s even possible to generate multiple images for an Astro collection. The same way Astro generates HTML pages from your collection, you can generate a cover image for each post. Suppose you have your posts inside &lt;code&gt;src/pages/[slug]/index.astro&lt;/code&gt;, you can create a new file named &lt;code&gt;src/pages/[slug]/cover.png.ts&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getStaticPaths&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; posts&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getCollection&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; posts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    params&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;slug&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;slug&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;APIRoute&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // do whatever you want with post - extract the content, excerpt, date, tags&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // load fonts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // create html markup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // create svg with satori&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // create png with sharp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // send response&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-meta-tags&quot;&gt;The &lt;code&gt;meta&lt;/code&gt; tags&lt;/h2&gt;
&lt;p&gt;One really final touch—don’t forget to add the proper meta tags:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; property&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;og:image&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;https://www.yieldcode.blog/og.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;twitter:card&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;summary_large_image&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;twitter:image&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;https://www.yieldcode.blog/og.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Two months of nomading</title><link>https://kudmitry.com/articles/two-months-of-nomading/</link><guid isPermaLink="true">https://kudmitry.com/articles/two-months-of-nomading/</guid><pubDate>Tue, 18 Jul 2023 22:00:00 GMT</pubDate><content:encoded>&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Me at my San Jose AirBnb&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;1066&quot; src=&quot;/_astro/two-months-nomading.DDA4Gz4t_Z18oRaN.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Me at my San Jose AirBnb&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;About two months ago, I quit my job.
I sold most of my stuff, packed the rest into a suitcase and a backpack, took my wife and our dog - and flew one way to Costa Rica 🇨🇷.&lt;/p&gt;
&lt;p&gt;My plan was simple — slow traveling combined with building a sustainable business.
Month and a half had passed, and I’m sitting right now at Juan Santamaria International Airport — waiting for a flight to my next destination.&lt;/p&gt;
&lt;p&gt;How would I sum up this period?
Two words — &lt;strong&gt;I failed&lt;/strong&gt;.
But as it’s written in one of the books my manager at Forter gave me, as a goodbye present, this was an intelligent failure.&lt;/p&gt;
&lt;p&gt;What does it mean?
It means that I’ve learned a lot.
How to (not) start a business.
What works and what does not work.
What are my strong traits, and what I should focus on next.
Failure is an inevitable part of eventually succeeding.
The people who succeed, are not the ones who never failed.
The people who succeed, are those that keep going — despite their failures.&lt;/p&gt;
&lt;p&gt;And I intend to keep going.&lt;/p&gt;
&lt;p&gt;Pictured: me at my San Jose Airbnb, finishing some stuff before packing up for my next destination.&lt;/p&gt;
&lt;p&gt;📍
&lt;i&gt;
Juan Santamaria International Airport
&lt;/i&gt;&lt;/p&gt;</content:encoded></item><item><title>Why engineers should focus on writing</title><link>https://kudmitry.com/articles/why-engineers-should-write/</link><guid isPermaLink="true">https://kudmitry.com/articles/why-engineers-should-write/</guid><pubDate>Sun, 09 Jul 2023 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;All engineers are good writers… of code. But I believe that in order to a become better engineer–you should improve your writing skills.&lt;/p&gt;
&lt;p&gt;From the dawn of times, people were writing. We have written using symbols, like in Ancient Egypt. And we have written using letters, like in Renaissance times. And all of us, got at least one writing assignment in school, without the “Why?” And yet, today writing is so underrated, that most people want to avoid it. But the truth is–you will have to write. Comments, documentation, design documents, presentations. Whether you like it or not. So why not become better at it?&lt;/p&gt;
&lt;h2 id=&quot;why-write&quot;&gt;Why write?&lt;/h2&gt;
&lt;h3 id=&quot;writing-is-a-way-to-organize-your-brain&quot;&gt;Writing is a way to organize your brain&lt;/h3&gt;
&lt;p&gt;For some reason we, humans, think that knowledge equals reading stuff. I’ve read the starting guide for Rust, so now I know this topic. I’ve read a book about how TCP/IP works–so now I know this stuff. But it’s not true. If it was true, we’d all be super-stars.&lt;/p&gt;
&lt;p&gt;What helps us cement our knowledge–is writing. That’s why I believe in &lt;strong&gt;writing&lt;/strong&gt; code, as opposed to copying code snippets. Because when you type it–you cement that knowledge.&lt;/p&gt;
&lt;h3 id=&quot;writing-is-a-way-to-learn-something&quot;&gt;Writing is a way to learn something&lt;/h3&gt;
&lt;p&gt;If you want to learn a new topic–write about it. When I want to learn a new programming language, I write a short program using that programming language. When I want to understand how something works–I write an article on this blog about it.&lt;/p&gt;
&lt;p&gt;Real writing, where you try to dig the truth, will require you to try and experiment. I’ve learned more by writing articles on this blog, than by reading programming books.&lt;/p&gt;
&lt;h3 id=&quot;writing-helps-you-identify-mistakes&quot;&gt;Writing helps you identify mistakes&lt;/h3&gt;
&lt;p&gt;How many times we are asked to prepare a design document, while we think to ourselves, “What for? The design is so simple, I can hold it in my head easily.” This is a big misconception, we all fall for. If we would actually write the design document, we would have identified so many problems with our “simple” design. Things like–inconsistencies, missing details, or simply “lazy” thinking our brain did, which in reality make no sense.&lt;/p&gt;
&lt;h2 id=&quot;how-to-write-more&quot;&gt;How to write more?&lt;/h2&gt;
&lt;p&gt;Remember–reading is a habit, writing is a skill. And in order to prefect your skill, you need to write more. One simple way to write more–is to approach design reviews differently. Instead of hating them, and doing them like a homework assignment, try to approach them with enthusiasm. Each time you write a design review, try to improve something. Make it shorter–without missing the point. Make it longer–in order to cover more use-cases.&lt;/p&gt;
&lt;p&gt;Writing design reviews, and documentations–at your workplace–is an easy way to get into writing. You will have to do it anyway, so why not improve while writing? However, if you want to improve your writing further, outside your workplace, consider starting a blog. If writing a blog is intimidating for you, consider answering questions on Stackoverflow–but focus on providing textual content, rather than copy-pasting code snippets.&lt;/p&gt;
&lt;p&gt;One last advice–abolish the copy-paste. So many developers, whom I mentored, simply copy-paste everything. Code snippets, function declarations, etc. I know how to initialize a git repository, because I do it by hand every time. Most people simply copy the instructions from GitHub or Google. And if you are scared about being unproductive–remember that you are not judged by how much code you write, or how fast you complete the assignment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Writing is unavoidable. Whether you like it or not, if you want to become a 10x engineer, you will have to write. Write documentations, write design reviews, write presentations. The sooner you get better at writing, the faster you will become a better engineer.&lt;/p&gt;</content:encoded></item><item><title>An Essay on Burnout</title><link>https://kudmitry.com/articles/an-essay-on-burnout/</link><guid isPermaLink="true">https://kudmitry.com/articles/an-essay-on-burnout/</guid><pubDate>Sun, 28 May 2023 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;14 years ago, I’ve got my first programming job. For 14 years, I worked mainly non-stop. I had some short breaks in-between jobs, but usually I’d end one job, and the next day would start a new one. I was literally waking up, and going to a different office the next day.&lt;/p&gt;
&lt;p&gt;I really loved what I do, and couldn’t wait to start working in a new and exciting company. I was unable to understand people who took breaks in-between. I was thinking to myself—“&lt;em&gt;Do people really hate their profession that much that they need to take breaks?&lt;/em&gt;” Moreover, any time someone mentioned the phrase “burnout”, I would cringe. I denied the existence of burnout as a concept. Sure, you could land on a bad manager, bad team, bad company, or exhaust your potential in a particular company—but those were perfectly fine reasons to move to a new workplace.&lt;/p&gt;
&lt;p&gt;I saw every person who said that they burnt out—as weak. If you hate your profession, then maybe you should do a career change. But life’s a funny thing. It will eventually bring you down in order to teach you a lesson. And here I am—burnt out. Ah, the irony.&lt;/p&gt;
&lt;h2 id=&quot;how-did-i-get-there&quot;&gt;How did I get there&lt;/h2&gt;
&lt;p&gt;Before my last job as software engineer at Forter, I worked for almost 5 years at Autodesk. It was my first big corporate job, although the Tel-Aviv office, where I worked, felt more like a well funded startup. I came to Autodesk as a contractor, and eventually moved to an FTE. I occupied different positions such as senior software engineer, and tech lead. I interviewed people; I mentored people; I helped onboarding new hires; I worked on at least 5 different projects, and changed 2 amazing managers; and I had the opportunity to work with talented people.&lt;/p&gt;
&lt;p&gt;But somewhere after 4 years, I was feeling that I reached the ceiling of my growth there, and it’s time to move on. It was also a crucial step for my career, because life gave me the book &lt;a href=&quot;https://www.amazon.com/Developer-Hegemony-Erik-Dietrich/dp/0692866809&quot;&gt;Developer Hegemony: The Future of Labor&lt;/a&gt; by Erik Dietrich, which I bought on 7th of October 2021, just 4 months before I’d find myself in a completely different company. It took me 17 days to finish this book, which for me, as a slow reader, is very fast. I recommend everyone who feels similar to what I’ll describe here—to read this book.&lt;/p&gt;
&lt;p&gt;After about 2-3 month of interviews with many interesting companies, I found a job with Forter. I handed out my 30-day notice to Autodesk, officially finished my relationship with them on 31st of January 2022, and on 2nd of February same year—I had my onboarding day at Forter. Fun fact, I did not intentionally take one day of rest. If I could, I would start on the 1st of February, but Forter has two days a week when they do onboarding, and 1st of February wasn’t one of them. So I had one day of rest between jobs. Crazy me.&lt;/p&gt;
&lt;p&gt;I was super excited to start my new journey. I really wanted to get into Forter, as they have a very strong engineering culture. Furthermore, I got into a new team with a new set of products, in a completely different market. A lot of new projects, and great opportunity to design, and architecture them from scratch. I’ve asserted my position as a strong engineer, with a clear desire to move up the career ladder.&lt;/p&gt;
&lt;p&gt;Fast-forward to one month ago, I handed out my 30-day notice, and at the time of publishing this essay—I’m officially jobless. My wife and I sold everything, took essential items, and booked a one-way ticket to Costa Rica. I’ve officially burnt out.&lt;/p&gt;
&lt;h2 id=&quot;what-went-wrong&quot;&gt;What went wrong&lt;/h2&gt;
&lt;p&gt;Burnout is hard to diagnose and distinguish from general exhaustion or stress. People change jobs for different reasons. Sometimes they are offered a more interesting product, other times a more senior title. And sometimes it’s about money. Not every software engineer is motivated by passion—for some, it’s just a job that pays well. I get it. It is possible to make these people stay at the company rather than switch, but I’ll leave that discussion to a different article.&lt;/p&gt;
&lt;p&gt;However, when I left Autodesk, I had different motives. I started to have a strong feeling of dissatisfaction. I had low energy, and lack of motivation. A feeling of exhaustion, as in “I have given everything I could, and I took everything I was able to take—and there is nothing else left for me there”. I started to distance myself from the company. I started to have dissatisfaction with the profession. I remember when I decided to quit, I had repulsion towards programming in both a professional environment, as well as a hobby. In my free time, I focused more on other hobbies such as reading, or writing—rather than coding.&lt;/p&gt;
&lt;p&gt;I noticed that I start to fall behind in performance. Tasks that I would to ace, now take more time to complete. I was no longer excited about my job, and I felt tired from just commuting to the office, let alone being there. And I had a feeling that if I won’t do something about it, I’ll end up in a deep rut, that might even lead to depression. Looking at it now, I was burnt out at that moment.&lt;/p&gt;
&lt;p&gt;Each single symptom does not necessarily mean that you have burnt out. However a full house might be a good indicator that you need to change something, either do a break or try a slight career change (for example moving away from high pressure FAANG like company, to something smaller).&lt;/p&gt;
&lt;p&gt;However, I failed to recognize this in time, and I’m bad with communicating my feelings to my manager. So I ended up in a corner, where quitting was the only sensible solution to me. And so I did. I found myself in a new company, and the excitement came back. I was devouring knowledge, proving myself in a new environment. The passion to coding came back. I’ve given talks to the community, I’ve learned new tools such as &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;tmux&lt;/code&gt; in order to improve my workflow.&lt;/p&gt;
&lt;p&gt;But quickly enough, I was back at the same place I’ve been at for the last 5–6 months in Autodesk. I got dissatisfaction with my work. I hated being in the office, and would feel sick there. I started to fantasize about having a different career such as: car mechanic, police detective, movie actor, podcaster, DJing or music production. Anything, but please not writing code anymore. Eventually it grew into apathy, and it took me a lot of mental energy just to care.&lt;/p&gt;
&lt;p&gt;I didn’t plan to quit, but life gave me an opportunity I couldn’t pass. Looking in retrospective, I probably had burnout by the time I left Autodesk. Switching jobs always creates this feeling of newness. But your wounds remain untreated.&lt;/p&gt;
&lt;h2 id=&quot;what-causes-burnout&quot;&gt;What causes burnout&lt;/h2&gt;
&lt;p&gt;It’s hard to understand why burnout happens. In the end, some people stay for 10, 15, or even 20 years in a company. They do, usually, move through the career ladder, but nevertheless they stay at the same company. However, I believe there are two main factors that contribute to burnouts.&lt;/p&gt;
&lt;h3 id=&quot;hustle&quot;&gt;Hustle&lt;/h3&gt;
&lt;p&gt;Work, today, is glorified. &lt;em&gt;What do you do in life?&lt;/em&gt;—is often the first question we ask new people we just met. And you better have an answer other than &lt;em&gt;just traveling and enjoying life&lt;/em&gt;, or you will be labeled as lazy, unmotivated, indolent child. We live in a hustle culture. If you don’t have a job—you are an immature adult.&lt;/p&gt;
&lt;p&gt;Hustle culture is on the raise. Wherever you look, you get the message that you need to hustle. Become someone. Work harder than anyone else. Stay up late. Show up earlier. In the end you will be rewarded for your hard work. Truth is—meritocracy does not exist. I used to believe it does, but the ones who are getting promoted, are rarely the ones who work the hardest.&lt;/p&gt;
&lt;h3 id=&quot;toxic-positivity&quot;&gt;Toxic positivity&lt;/h3&gt;
&lt;p&gt;We also live in an era, of what I call, toxic positivity. We tend to avoid negative emotions, and try to avoid people who share their struggles. While I never felt it directly, its societal existence had an impact on me. The desire to run away from negative emotions, leads us to believe that the problem is external. It’s not me, who burnt out, but rather the company that is not valuing me, promoting me, appreciates me, etc.&lt;/p&gt;
&lt;p&gt;Moreover, there is a societal judgment toward the ones who make good money, by doing, what seems like, an easy job, in-between playing ping-pong, and drinking beer. You often hear, or read phrases like “oh those poor developers who complain about the fact that have to carry their shiny MacBooks on a Saturday evening, because they are on-call—there are people who are stuck in a dead end jobs, getting paid minimum wage.” And I agree, the latter is terrible. But it does not deduct any pain, or suffering, from me. My feelings can’t be ignored because somebody has it worse than me. No one should have his, or her feelings—ignored. That’s not how we create a healthy society.&lt;/p&gt;
&lt;p&gt;And so we are not allowed to feel bad, or express struggle. And we find someone else to blame—an external entity, such as an employer. Therefor, the solution is also external—change job. By changing jobs, I’ll get a fresh start to prove myself, and get what I deserve. And I better make the change ASAP, without any breaks in between, because I don’t want to be labeled as lazy, unmotivated, and indolent child who is &lt;em&gt;just enjoying life, while figuring what to do next&lt;/em&gt;. Yikes!&lt;/p&gt;
&lt;p&gt;But in the end, work is just work. Recent layoffs, and economic recession, proved—once again—that people, at large, are a disposable resource. When times are good, we will be partying in Hawaii and posting inspiring pictures in LinkedIn, promoting our fun office vibe. But when times are thought, we will be hoping that we won’t be laid off, because being able to pay mortgage is, apparently, way more important than drinking Piña colada in Hawaii.&lt;/p&gt;
&lt;h3 id=&quot;tech-industry&quot;&gt;Tech Industry&lt;/h3&gt;
&lt;p&gt;Apart from those two factors, there are other, unique, factors specific to the tech industry. Because this industry is extremely fast-paced, this leads to a situation where one could be working on a project with no clear definition. Companies like to put &lt;em&gt;can work in an uncertain, fast-paced environment&lt;/em&gt;, in their job descriptions, and while it is ok to have such environment for a limited period of time—if an individual works on an unclear goal, or his project gets canceled every time, this might create a situation where he or she starts to feel disengaged, and unfulfilled at work.&lt;/p&gt;
&lt;p&gt;And this puts on a lot of stress on the individual. He can’t fulfill himself at work, which in turn leads to a feeling of emptiness, which slowly creates detachment from work. On a professional level, he could feel stress because on the upcoming performance review he has nothing to show, since his project got cancelled again.&lt;/p&gt;
&lt;p&gt;Apart from the uncertain environment, and unclear goals, another stress factor could be the abundance of meaningless rituals like daily standups, weekly retrospectives, and monthly status updates—which create a mental fatigue that feeds this cycle of inability to do any work, which leads to stress, and eventually burnout.&lt;/p&gt;
&lt;h2 id=&quot;how-to-prevent-burnout&quot;&gt;How to prevent burnout&lt;/h2&gt;
&lt;p&gt;I remember we had a Tech Lead forum at Autodesk, where all tech leads would gather, on a weekly (or it was bi-weekly) basis, and discuss tech lead stuff. Sometimes it would be struggles from inside the teams, other times we would talk about software architecture.&lt;/p&gt;
&lt;p&gt;I remember one session where somehow we came to the topic of problem prevention versus problem solving, and the entire forum got split into one of two camps. Long story short, I don’t believe in prevention. This is simply not how life works, in my experience. However, I can shed some light on how to identify and cure burnout.&lt;/p&gt;
&lt;p&gt;One pretty accurate model, that I read about on HackerNews, says that there are 5 phases to burnout. First, you have the honeymoon phase. You join a company, everything is pink and chocolate. Then comes the realistic phase—you realize that not everything is good, and there are bad sides as well. The next phase is team-isolation phase—my team is great, but everyone else is stupid/lazy/ignorant. The fourth phase is self-isolation—I’m doing my best, but everyone else is stupid/lazy/ignorant. And the last phase is apathy—I don’t care anymore.&lt;/p&gt;
&lt;p&gt;This model was described by &lt;code&gt;sz4kerto&lt;/code&gt; on &lt;a href=&quot;https://news.ycombinator.com/item?id=31727289&quot;&gt;HackerNews&lt;/a&gt;, and I think it’s very accurate, based on my experience. That’s exactly what I felt when I quit Forter. I remember the apathy. I just scheduled 1:1 with my manager and told him that I quit. No emotions. Funny thing is, he told me he had a feeling. When I asked him why, he told me I had an attitude change.&lt;/p&gt;
&lt;p&gt;Each stage by itself does not indicate that you are going towards burnout. It is possible to feel that your team is good, but other teams are not supportive. It could just mean that you’ve ended up in a bad company, and not that you’ve burnt out. And it is only when you look back, and you are able to identify all the 5 stages—you realize that you have burnt out. That’s why it’s so hard to prevent burnout.&lt;/p&gt;
&lt;p&gt;At that point, I’d recommend a change of some sort. Maybe a break, or downgrade to a less demanding job. Also, try to take care of yourself. More often than not, burnout at work, also leads to deterioration in your day-to-day life. You eat shit, because you are working all the time (or try to appear like you are working). You don’t take care of your physical state. And during any free time you get, you feel exhausted, and unable to do any hobbies. Your mind is occupied by the future thought of going back to work tomorrow, or on Monday. Stress levels are also high, so read about stress management, and apply it.&lt;/p&gt;
&lt;p&gt;If you are unable to take a break, and don’t want to or can’t find a less demanding job, then maybe you do need a career change.&lt;/p&gt;
&lt;p&gt;And please talk. Talk to your manager. Talk to your friends and family. &lt;strong&gt;Reach out to a professional therapist&lt;/strong&gt; if you can’t handle it yourself. Unresolved burnout can lead to depression.&lt;/p&gt;
&lt;h2 id=&quot;what-is-next&quot;&gt;What is next&lt;/h2&gt;
&lt;p&gt;For the first time in my adult life—I’m jobless. I got my first real job when I was still in college, and since then I’ve been working. It’s a weird feeling, to not having to go to work. And there is a strange post-traumatic syndrome. I remember a day or two after I was already jobless, my wife asked me if I want to go watch a movie midday. My first reaction was a mini panic, as-in: “How am I going to disappear from work for 2 hours, in the middle of the day?”. Work &lt;strong&gt;is&lt;/strong&gt; an important part of our life.&lt;/p&gt;
&lt;p&gt;As for me, I’m going to go inwards. The problem was never Autodesk, or Forter, or the industry. The problem—is me. And accepting that is not a shame. I’m going to focus on projects that excite me. I have few interesting ideas for tools for developers. I’m going to focus on this blog, as well as my second blog—&lt;a href=&quot;https://jikokaizen.blog&quot;&gt;Jiko Kaizen&lt;/a&gt;. I’m going to focus on my health, both physical and mental. And I’m going to spend time with my wife, exploring together new places. I’ll see where all this takes me. I might return to a traditional workplace, however I plan not to.&lt;/p&gt;
&lt;p&gt;I lived in denial, looking from above at all those people who burnt out. In denial of the existence of burnout. But it’s real. And I want to raise awareness to that. You, yes you! You might be experiencing burnout right now! But the societal norm—or the story you created to yourself, about yourself—are preventing you from realizing it, because you need to be strong, quit whining, and work hard to become someone in this world. But the truth is—you don’t. You deserve to be happy.&lt;/p&gt;
&lt;p&gt;Stay safe. Take care of your mental health. Nos Vemos!&lt;/p&gt;</content:encoded></item><item><title>The need for a more semantic web</title><link>https://kudmitry.com/articles/the-need-for-a-more-semantic-web/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-need-for-a-more-semantic-web/</guid><pubDate>Fri, 17 Feb 2023 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The web has come a long way. From the early static pages, through the evolution of reactivity, and towards the biggest knowledge pool for all of humanity.&lt;/p&gt;
&lt;h2 id=&quot;a-little-history-of-the-web&quot;&gt;A little history of the web&lt;/h2&gt;
&lt;p&gt;The World Wide Web (WWW), which is commonly known as the Web, was invented by Tim Berners-Lee at CERN in 1989. It’s an information system that enabled documents and other web resources such as—images, videos, music, etc—to be accessed over the internet.&lt;/p&gt;
&lt;p&gt;I, myself, being an early user of the web, saw it transform through different generations and paradigms. I like to call them Web 1.0, Web 2.0 and Web 3.0. Let’s explore the unique characteristics of each of those eras.&lt;/p&gt;
&lt;h3 id=&quot;web-10&quot;&gt;Web 1.0&lt;/h3&gt;
&lt;p&gt;When I talk about Web 1.0, I refer to the early state of the Web. The state where there was practically no standard and no interactivity. Web pages, back then, used to be completely static and written entirely in HTML (which was poorly defined as a standard back then). CSS was non-existent (it was created later, in 1996). JavaScript, which appeared in 1995, was also unheard of.&lt;/p&gt;
&lt;p&gt;This was the era of the static, generic looking, web. This is how the &lt;a href=&quot;http://info.cern.ch/hypertext/WWW/TheProject.html&quot;&gt;first ever website&lt;/a&gt; looked like.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/first-ever-website_dqzx4n.webp&quot; alt=&quot;First ever website#1424x546&quot;&gt;&lt;figcaption&gt;First ever website#1424x546&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Go on, check the source of it.&lt;/p&gt;
&lt;h3 id=&quot;web-20&quot;&gt;Web 2.0&lt;/h3&gt;
&lt;p&gt;With the birth of CSS and JavaScript, the world entered a new era of the Web. The Web 2.0. This web provided more colors. This web provided interactivity.&lt;/p&gt;
&lt;p&gt;Suddenly, people started to care about separation between structure (the HTML side) and appearance (the CSS side). HTML elements such as &lt;code&gt;&amp;#x3C;font&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;marquee&gt;&lt;/code&gt;, and &lt;code&gt;&amp;#x3C;strong&gt;&lt;/code&gt;—became deprecated, and were replaced with their CSS and JS counterparts.&lt;/p&gt;
&lt;p&gt;The Web 2.0 also saw the birth of the famous &lt;code&gt;XMLHttpRequest&lt;/code&gt; and the &lt;code&gt;Ajax&lt;/code&gt; technology. You could replace an entire webpage, or parts of it, without navigating away from the page—which created a feeling of what today is known as SPA—Single page application.&lt;/p&gt;
&lt;p&gt;On the HTML side, we saw the appearance of some semantic attributes such as &lt;code&gt;&amp;#x3C;article&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;section&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;aside&gt;&lt;/code&gt;, and others. Finally, in 2008, HTML5 was released, which among many things, added a support for &lt;strong&gt;Microdata&lt;/strong&gt;. Remember this, as we will talk about Microdata later.&lt;/p&gt;
&lt;p&gt;With the rise of the Web 2.0, came the rise in content published in the World Wide Web. Suddenly it became easier, cheaper, and more accessible to have a web page. People, and companies, started to care about web presence. And more over—the Web became a place of knowledge. This era gave birth to concepts such as blogging and forums. In 2001—Wikipedia was born. And as the amount of content grew, the need to find content became a necessity. In 1998 Google was created, and later became the dominant search engine till this day.&lt;/p&gt;
&lt;p&gt;Web 2.0 was a great era. An era of revolution in the way we present our webpages, as well as revolution in the way the Web is being used. But something happened, which ended the Web 2.0 era, and Web 3.0 became the new kid in the town.&lt;/p&gt;
&lt;h3 id=&quot;web-30&quot;&gt;Web 3.0&lt;/h3&gt;
&lt;p&gt;When we hear the term Web 3.0, most of us think about decentralized web, Bitcoin, NFTs, and blockchain. But in my opinion those technologies are unrelated to the Web. The Web was, and still is, a decentralized system. Yes, it is controlled by few major corporations. But nothing forbids me to buy a server, put it in a rack at my home, and plug in into the internet in order to serve my blog from it.&lt;/p&gt;
&lt;p&gt;For me, Web 3.0 means the birth of the &lt;strong&gt;semantic web&lt;/strong&gt;. Somewhere around the peak popularity of Facebook, the Web stopped being about webpages, and started to become more about &lt;strong&gt;things&lt;/strong&gt;. When I search for &lt;code&gt;@skwee357&lt;/code&gt;, I don’t want to find webpages that contain this obscure phrase/word. I want to find a person behind this handle (which is me). When I search for “Fight club”, I want to find the movie by David Fincher. The search term, “how to make pancakes”, should ideally yield a set of recipe results for pancakes.&lt;/p&gt;
&lt;p&gt;Web 3.0 is about things. It’s no longer about webpages and content. It’s about people, movies, recipes, tutorials, places, events, books. And the current state of things with Web 3.0—has one drawback.&lt;/p&gt;
&lt;h2 id=&quot;the-birth-of-the-semantic-web&quot;&gt;The birth of the semantic web&lt;/h2&gt;
&lt;p&gt;As I’ve noted earlier, during the Web 2.0 era, HTML got some new elements that aimed to make it more semantic. &lt;a href=&quot;http://www.tutorialspoint.com/html5/html5_new_tags.htm&quot;&gt;Here is a list of some of them&lt;/a&gt;. But have you ever wondered what’s the different between a &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; element and an &lt;code&gt;&amp;#x3C;article&gt;&lt;/code&gt; element? &lt;code&gt;&amp;#x3C;span&gt;&lt;/code&gt; vs &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; is a simple answer. The first does not create a new block, and can be incorporated inside a sentence without breaking it. But most of the other elements, such as &lt;code&gt;&amp;#x3C;article&gt;&lt;/code&gt; or &lt;code&gt;&amp;#x3C;header&gt;&lt;/code&gt;, do not have different behavior than say a &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;They do serve one major role—to help make the web more semantic. With the increased usage of the Web, came increased production of web content. And the latter created a demand for increased searching and indexing capabilities. And these semantic HTML elements—help search engines to classify your content. When a crawler sees an &lt;code&gt;&amp;#x3C;article&gt;&lt;/code&gt; element—it knows that the text, inside of it, is indeed an &lt;strong&gt;article&lt;/strong&gt;. Compare that to a generic &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; which does not provide any information about the nature of it’s content. It could be an article, but also could be a table of contents.&lt;/p&gt;
&lt;p&gt;Have you noticed how Google sometimes provide a snippet from Wikipedia, or recommend you a video from YouTube or Vimeo? How does Google, or any other search engine, knows to classify between a general section on your web page, and the content of the blog post? Probably by looking at the &lt;code&gt;&amp;#x3C;article&gt;&lt;/code&gt; HTML element. But there is more. How does a search engine know that “Brad Pitt” is an actor, “Fight Club” is a movie, and “1984” is a novel?&lt;/p&gt;
&lt;h3 id=&quot;semantic-semi-standards&quot;&gt;Semantic semi standards&lt;/h3&gt;
&lt;p&gt;Like with any new and established standard, someone first created a semi-standard. And this is what happening to Web 3.0 now. I believe that Facebook was the first to create the &lt;code&gt;og&lt;/code&gt; meta attributes to classify content. A webpage with correct &lt;code&gt;&amp;#x3C;meta&gt;&lt;/code&gt; tags with &lt;code&gt;og&lt;/code&gt; attributes—will provide semantic information to those who parse the &lt;code&gt;og&lt;/code&gt; meta attributes. One could provide information such as site name, description, associated image, etc. The &lt;code&gt;og&lt;/code&gt; attributes are responsible for content sharing. When you post a link to Facebook or LinkedIn, those platforms query the &lt;code&gt;og&lt;/code&gt; attributes in order to extract the title of the webpage, description, as well as cover image.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;meta&lt;/code&gt; attributes are limiting. And so other formats were created. I believe the first among them was &lt;a href=&quot;https://en.wikipedia.org/wiki/Resource_Description_Framework&quot;&gt;RDF&lt;/a&gt;—Resource Description Framework. RDF never gain much popularity, and was served as a foundation to newer semi-standards such as JSON-LD and Microdata, which I mentioned earlier when I wrote about HTML5. Those two formats took a different approach.&lt;/p&gt;
&lt;h4 id=&quot;microdata&quot;&gt;Microdata&lt;/h4&gt;
&lt;p&gt;Microdata was targeting to enhance existing HTML elements. By using Microdata, one can provide a set of special attribute to any HTML element. Combining with a set of predefined schemas, such as &lt;a href=&quot;https://schema.org/Event&quot;&gt;event&lt;/a&gt;, &lt;a href=&quot;https://schema.org/Product&quot;&gt;product&lt;/a&gt;, and &lt;a href=&quot;https://schema.org/Review&quot;&gt;review&lt;/a&gt;—one can provide additional context to crawlers about the nature of the content on a webpage. A geolocation with Microdata will look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; itemprop&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;geo&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; itemscope&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; itemtype&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;https://schema.org/GeoCoordinates&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; itemprop&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;latitude&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;52.48&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; itemprop&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;longitude&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;-1.89&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interestingly enough, according to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata&quot;&gt;MDN&lt;/a&gt;, Microdata support was removed from Firefox 49. According to &lt;a href=&quot;https://en.wikipedia.org/wiki/Microdata_(HTML)&quot;&gt;Wikipedia&lt;/a&gt;—no major browser support the Microdata DOM API. &lt;a href=&quot;https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data?visit_id=638120872435583159-706353772&amp;#x26;rd=1&quot;&gt;Google developers&lt;/a&gt; portal tells us that Google, still, does care, and parse, Microdata.&lt;/p&gt;
&lt;h4 id=&quot;microformats&quot;&gt;Microformats&lt;/h4&gt;
&lt;p&gt;Somewhere around the time Microdata became a thing, another semi-standard, that’s called Microformats—was created. Microformats took a similar approach to Microdata, but instead of relying on a good set of schemas to classify things (like the above event, product, or review I’ve mentioned), it went on to define custom CSS classes. So a geolocation would be marked as:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;h-geo&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;p-latitude&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;52.48&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;p-longitude&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;-1.89&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach created some problems. For starters, there was no schema definition for &lt;em&gt;thing&lt;/em&gt;. A crawler would have to implement all the &lt;code&gt;h-*&lt;/code&gt; and corresponding &lt;code&gt;p-*&lt;/code&gt; classes. Extending Microformats was practically impossible. In addition to that, the usage of CSS classes to classify content semantically—feels wrong.&lt;/p&gt;
&lt;h4 id=&quot;json-ld&quot;&gt;JSON-LD&lt;/h4&gt;
&lt;p&gt;Lastly, we need to talk about JSON-LD. JSON-LD is also a semantic web format. But instead of trying to build upon existing convention—like HTML element attributes, or CSS classes—JSON-LD relies on encoding your entire semantic information, in a &lt;code&gt;script&lt;/code&gt; tag. Here’s how it looks for the &lt;a href=&quot;/&quot;&gt;homepage&lt;/a&gt; of my blog:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;application/ld+json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;@context&quot;: &quot;https://schema.org&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;@type&quot;: &quot;Blog&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;@id&quot;: &quot;https://www.yieldcode.blog&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;name&quot;: &quot;yield code();&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;description&quot;: &quot;Thoughts and stories on programming, the industry, and technology written by a software engineer&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;author&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;@type&quot;: &quot;Person&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;@id&quot;: &quot;https://www.kudmitry.com/&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;name&quot;: &quot;Dmitry Kudryavtsev&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;sameAs&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;https://www.kudmitry.com/&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;https://linkedin.com/in/kudmitry&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;http://github.com/skwee357&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;http://twitter.com/skwee357&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;image&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;@type&quot;: &quot;ImageObject&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;url&quot;: &quot;https://res.cloudinary.com/dvp7olno1/image/upload/v1673035861/yieldcode/cover_eacuil.jpg&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;height&quot;: &quot;150&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &quot;width&quot;: &quot;150&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &quot;keywords&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;programming&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;software industry&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;software engineering&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;tutorials&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;programming consulting&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;tech lead&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &quot;architect&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSON-LD is one of the most popular semantic web formats, and it is the one recommended by Google. RDF and Microdata never became popular. Microformats has a small community among &lt;a href=&quot;https://indieweb.org/&quot;&gt;IndieWeb&lt;/a&gt; people, but it’s hardly a specification. It’s a gross hijack of CSS classes—that were created to style the HTML elements—not provide semantic information.&lt;/p&gt;
&lt;h2 id=&quot;making-web-30-great&quot;&gt;Making Web 3.0 great&lt;/h2&gt;
&lt;p&gt;HTML is an established format and specification. By looking at this webpage, you already have most of the semantic information you need. It makes no sense to duplicate the same semantic information inside a JSON in the HTML &lt;code&gt;&amp;#x3C;header&gt;&lt;/code&gt; tag, for the sake of crawlers. This creates unnecessary duplication, which in turn makes the webpage heavier.&lt;/p&gt;
&lt;p&gt;However, with the rise of Web 3.0 and the content era, we have to help crawlers understand our content better. The web needs to become cleaner for machines, so it will become cleaner for humans. If we introduce HTML elements such as &lt;code&gt;tag&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;recipe&lt;/code&gt;, and others—we can create a better web for machines, which will help to create a better web for us—humans.&lt;/p&gt;
&lt;h2 id=&quot;call-to-action&quot;&gt;Call to action&lt;/h2&gt;
&lt;p&gt;Web Hypertext Application Technology Working Group, also known as WHATWG, is a community of people interested in evolving the HTML. About a month ago, I’ve created a proposal at WHATWG Github: &lt;a href=&quot;https://github.com/whatwg/html/issues/8693&quot;&gt;Elements for a more semantic web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The web is built on open formats. The evolution of HTML is needed in order to keep up with the changing nature of the Web. I hope I was able to convince you that we need to move HTML forward, and adapt it to the new reality of the Web 3.0—the semantic web. Failing to do so, will force us to use formats that were created, and controlled, by corporations—better built to serve their needs, rather than ours. In addition to that, by failing to provide semantic information to crawlers, we will either have to place our hope in the hands of AI classification tools (which are &lt;a class=&quot;internal new&quot; href=&quot;/articles/chatgpt-ai-and-the-future-of-tech/&quot;&gt;far from optimal&lt;/a&gt;), or worse—drown in a sea of search results that make no sense.&lt;/p&gt;
&lt;p&gt;If I did convince you, then please go to &lt;a href=&quot;https://github.com/whatwg/html/issues/8693&quot;&gt;my proposal&lt;/a&gt; and show your support. Together we will move HTML to its next level.&lt;/p&gt;</content:encoded></item><item><title>Inheritance, Composition, and everything in–between</title><link>https://kudmitry.com/articles/inheritance-composition-and-everything-in-between/</link><guid isPermaLink="true">https://kudmitry.com/articles/inheritance-composition-and-everything-in-between/</guid><pubDate>Wed, 25 Jan 2023 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There are many concepts in the OOP paradigm. Inheritance is the most known, and it allows us to model software as relations between objects, just like in real life.&lt;/p&gt;
&lt;p&gt;But it has some drawbacks, which alternative paradigms can fix. Let’s dive into OOP, inheritance, and everything in–between.&lt;/p&gt;
&lt;h2 id=&quot;object-oriented-programming&quot;&gt;Object-oriented programming&lt;/h2&gt;
&lt;p&gt;Object-oriented programming, or OOP in short, is probably one of the most popular programming paradigms. It’s an easy paradigm to understand, because it follows the way we, humans, are seeing the world.&lt;/p&gt;
&lt;p&gt;For us—the world is a collection of objects that can be modeled in software. Let’s say a car. A car consists of the engine, a set of tires, a chassis, a steering wheel, seats, etc. In code, this would look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Engine&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; goVroom&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; SteeringWheel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Car&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; engine&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Engine&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; tires&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; steeringWheel&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; SteeringWheel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;inheritance&quot;&gt;Inheritance&lt;/h3&gt;
&lt;p&gt;We can’t talk about OOP without mentioning inheritance. Let’s look back at our car example. Say we want to output the parameters of our car. For the sake of simplicity, let’s omit the &lt;code&gt;Engine&lt;/code&gt; for now.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getInfo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; `Tire Radius: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;radius&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; circumference&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Math&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;PI&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; SteeringWheel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getInfo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; `Steering Wheel Radius: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;radius&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; circumference&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Math&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;PI&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can notice some repetition in there. If only there was a way to specify that both the &lt;code&gt;Tire&lt;/code&gt; and &lt;code&gt;SteeringWheel&lt;/code&gt; are forms of a &lt;code&gt;Circular&lt;/code&gt; object. Welcome—Inheritance.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;abstract&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; CircularObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; circumference&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Math&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;PI&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;radius&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Tire&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; CircularObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; SteeringWheel&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; CircularObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inheritance allows us to define common behavior and functionality, as well as common interface. We can enforce, through inheritance, that all object that inherit from &lt;code&gt;CircularObject&lt;/code&gt; will have a &lt;code&gt;radius&lt;/code&gt; property, and a &lt;code&gt;circumference&lt;/code&gt; property, which is calculated. I’m not going to go too deep into inheritance in this post, though.&lt;/p&gt;
&lt;h3 id=&quot;where-inheritance-fails&quot;&gt;Where inheritance fails&lt;/h3&gt;
&lt;p&gt;Inheritance, however, has one big pitfall—multiple inheritance.&lt;/p&gt;
&lt;p&gt;Let’s say we have the following code:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;abstract&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Apricot&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Apricot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Pluot is a real crossover between plum and apricot. It has high sugar levels and extraordinary sweet taste.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you try to “compile” that code, you will get an error:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Classes can only extend a single class.(1174)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And it’s worth mentioning that this is not some weird constraint of a Typescript compiler. Apart from C++ (and apparently python), no other major language supports multiple inheritance. Why you ask? There is one problem with it.&lt;/p&gt;
&lt;h4 id=&quot;the-diamond-problem&quot;&gt;The diamond problem&lt;/h4&gt;
&lt;p&gt;One of the biggest drawbacks of multiple inheritance, is that it can lead to one particular problem knows as—the diamond problem. Let’s try to visualize the above inheritance as a relations graph:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	Fruit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	 /\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	/  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   /    \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; Plum  Apricot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   \    /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    \  /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     \/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Pluot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this inheritance creates a diamond-like shape. The shape itself is not the problem, but the problem lies in an underlying implementation ambiguity one can introduce. Imagine that everything we inherit from &lt;code&gt;Fruit&lt;/code&gt;, needs to implement an abstract method called &lt;code&gt;getColor&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;abstract&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; abstract&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;purple&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Apricot&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Fruit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;yellow-orangish&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Apricot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;Pluot&lt;/code&gt; extends non-abstract classes, it is not obliged to implement the &lt;code&gt;getColor&lt;/code&gt; method. Assuming multiple inheritance was possible in Typescript, what would be the output of:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; pluot&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;pluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s impossible to know. This ambiguity is hard to mitigate. In complied language—it’s usually forbidden at the compilation level, either via explicit error during class inheritance definition, or during invocation. The latter enforces the invocation to be explicitly cast to a specific type, for example:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Pluot&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; pluot &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;System&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;println&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((Apricot)&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;pluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; // yellow-orangish&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Java does not support multiple inheritance either, so this code is for demonstration purposes only.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Another approach some languages take—is a compile time requirement to re-implement the method in the grand-child class, thus eliminating the ambiguity.&lt;/p&gt;
&lt;p&gt;However, in case of dynamic languages—nothing guards your types. In–fact, the JavaScript runtime doesn’t know about your &lt;code&gt;Apricot&lt;/code&gt; and &lt;code&gt;Pluot&lt;/code&gt; objects. All it sees are &lt;code&gt;Object&lt;/code&gt; types with a function on their prototypes. In this case, Typescripts compiler is the one guarding you. And Typescript, as well all know, is just a syntactic sugar on top of JavaScript.&lt;/p&gt;
&lt;h3 id=&quot;but-what-about-interfaces&quot;&gt;But what about interfaces?&lt;/h3&gt;
&lt;p&gt;Apart from abstract classes that can contain both interface and functionality, there are also what is known as Interfaces or Protocols. An interface is just a definition of an agreed upon API. It defines the API, but does not allow implementation in the body of the interface. It’s up to the object that implements the interface—to provide the actual implementation.&lt;/p&gt;
&lt;p&gt;Therefor, it’s possible to create a diamond like relation graph with interfaces. Consider the following example:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Target&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ...;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Clickable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	getTarget&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Target&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	getTarget&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Target&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Ball&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; implement&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Clickable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	getTarget&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; target&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ...;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; target&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since neither &lt;code&gt;Clickable&lt;/code&gt;, nor &lt;code&gt;Moveable&lt;/code&gt; provide the actual definition, it is perfectly fine that the ambiguity exists, and it does not create any problems, since the compiler has no confusion.&lt;/p&gt;
&lt;p&gt;The same is true for this scenario:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	move&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Ball&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	move&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;		...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Basketball&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Ball&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Basketball&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;move&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code complies and runs perfectly, because from the view point of the compiler, &lt;code&gt;Basketball&lt;/code&gt; conforms to the interface &lt;code&gt;Moveable&lt;/code&gt; since it inherits the &lt;code&gt;move&lt;/code&gt; method from &lt;code&gt;Ball&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are some more interesting cases, such as: what happens if the signature of &lt;code&gt;move&lt;/code&gt; in the &lt;code&gt;Moveable&lt;/code&gt; interface—is different from the &lt;code&gt;move&lt;/code&gt; in &lt;code&gt;Ball&lt;/code&gt;? Would you be able to both extend from &lt;code&gt;Ball&lt;/code&gt; and implement &lt;code&gt;Moveable&lt;/code&gt;? Which version of &lt;code&gt;move&lt;/code&gt; then will be called?&lt;/p&gt;
&lt;p&gt;I don’t want to spend more time on OOP and inheritance. Drop me a message/email (contact details down below this post) if you are interested to read more about those weird cases in Typescript. For now, let’s move on.&lt;/p&gt;
&lt;h2 id=&quot;mixins&quot;&gt;Mixins&lt;/h2&gt;
&lt;p&gt;As we’ve seen earlier, multiple inheritance is a big no–no. However, there are legit cases where we want to inherit functionality from multiple classes. A great example is the game development industry. It’s common to think of game objects as, well, objects. Let’s say we are trying to build a modular system of NPCs (Non Playable Character—represents a game character that is controller by the game/AI and not by the player).&lt;/p&gt;
&lt;p&gt;We have some NPCs that can move. Some NPCs can take damage. Not all NPCs are going to take damage. The local town potion seller—should not be attackable by the player. An evil orc or mimic chest—should be able to take damage. The chest however can’t move, because it’s, well, a chest.&lt;/p&gt;
&lt;p&gt;I bet in your head you already have an inheritance graph. Let’s try to model it.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; positionX&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; positionY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;		this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;positionX&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;		this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;positionY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TakesDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; health&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; takeDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;		this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; isDead&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; MimicChest&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TakesDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; PotionsSeller&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TakesDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh-no! The evil orc is trying to do multiple inheritance. Even though it does not introduce any ambiguity, like we’ve seen before, multiple inheritance is still forbidden. What can we do?&lt;/p&gt;
&lt;p&gt;Meet—Mixins. Mixin is a way to have reusable code, like our &lt;code&gt;Moveable&lt;/code&gt; and &lt;code&gt;TakesDamage&lt;/code&gt; classes, without having the end class to inherit both, hence avoiding the diamond problem.&lt;/p&gt;
&lt;p&gt;A common way to implement a mixin, is by creating functions that return an anonymous class. In our case, it will look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (...&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    _positionX&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    _positionY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionX&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionX&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; TakesDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    _health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    takeDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; health&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_health&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    get&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; isDead&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; NonPlayableCharacter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;TakesDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;NonPlayableCharacter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; orc&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;sluggish&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`Orc &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; is located at &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;takeDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  `Orc &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; took 20 points of damage and is now &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isDead&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;dead&quot;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;still alive&quot;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output of this program will be:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Orc&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; sluggish&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; located&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; at&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 0,0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Orc&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; sluggish&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; took&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 20&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; points&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; damage&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; now&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dead&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And as you can see, our &lt;code&gt;EvilOrc&lt;/code&gt; class was able to inherit from two classes! There are some drawbacks, as you probably noticed. For one—any mixin must have a generic constructor with &lt;code&gt;...args&lt;/code&gt;. Attempt to add a non-generic constructor to a mixin will result in error &lt;code&gt;TS2545&lt;/code&gt; that says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A mixin class must have a constructor with a single rest parameter of type ‘any[]’.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So all the benefits of hidden fields, or initialization—are thrown away. One way to at least remove some code duplication, is to have factory methods:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Vector2D&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; createOrc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;initialPos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; orc&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionX&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; initialPos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_positionY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; initialPos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; orc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; orc&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; createOrc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;sluggish&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But data access is still not protected. Fields can’t be marked as private, because then you lose access to initialize them, and need to provide a setter method—which in this case beats the entire purpose of making the field private in the first place.&lt;/p&gt;
&lt;p&gt;However, if you have common functionality that you want to share to between multiple objects, and don’t care much about encapsulating the properties—then mixins are a great way to do that.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;There is a way to define mixin through prototype inheritance. It’s ugly. But if you want to take a look, there is an example in the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/mixins.html#alternative-pattern&quot;&gt;Typescript Handbook&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As we’ve seen, with mixins you lose the encapsulation, and more over, you can encounter weird bugs like prototype overrides! Consider the following example:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (...&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;purple&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Apricot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Base&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;yellow-orangish&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; BasePluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Plum&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Apricot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;BasePluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Pluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What would be the output? The correct answer is &lt;code&gt;purple&lt;/code&gt;. However, if we switch the order if the mixin composition from &lt;code&gt;Plum(Apricot(BasePluot));&lt;/code&gt; to &lt;code&gt;Apricot(Plum(BasePluot));&lt;/code&gt;, the output now changes to &lt;code&gt;yellow-orangish&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;And what if we want to define a color for our &lt;code&gt;Pluot&lt;/code&gt;?&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; BasePluot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getColor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;red&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You won’t be able to access it, since mixins are based on prototype overriding.&lt;/p&gt;
&lt;p&gt;Last, but not least—have you noticed that we need to have 2 classes for every object we want to construct? In order to create a &lt;code&gt;Pluot&lt;/code&gt;, which is composed of &lt;code&gt;Apricot&lt;/code&gt; and &lt;code&gt;Plum&lt;/code&gt;, we need to have both the &lt;code&gt;BasePluot&lt;/code&gt; class, and the result of the mixin composition call. And how the heck do we create an instance of Plum or Apricot? That’s how:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ApricotBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ApricotClass&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Apricot&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ApricotBase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; ApricotClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Such a mess. Imagine testing a mixin, you will need to define a dummy class just to be able to instantiate the mixin itself. Mixins are better reserved for small pieces of reusable code that needs to be composed. In a real scenario, like a game—they are hard to maintain.&lt;/p&gt;
&lt;h2 id=&quot;composition&quot;&gt;Composition&lt;/h2&gt;
&lt;p&gt;Lastly, we come to Composition. Composition, as the name implies, is used to compose objects from reusable code pieces, rather than leveraging inheritance. Mixin is a form of composition, but it still relies on inheritance to combine shared functionality into one class.&lt;/p&gt;
&lt;p&gt;You’ve probably used composition many times in your development, without even knowing it. The &lt;code&gt;Car&lt;/code&gt; example at the top of the article uses composition, to compose one specific object—the &lt;code&gt;Car&lt;/code&gt;, using other reusable objects like &lt;code&gt;Tire&lt;/code&gt;, &lt;code&gt;Engine&lt;/code&gt;, etc. But this composition approach does not help us with our &lt;code&gt;EvilOrc&lt;/code&gt; example. We could do something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;		...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; moveable&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;	moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;		this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;moveable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it’s ugly, and repetitive.&lt;/p&gt;
&lt;p&gt;When building an architecture around composition, we no longer think of objects and their properties—as inheritance tree. We don’t say that an Orc is a Moveable object, and an object that can take damage. Instead, we say that an Orc &lt;strong&gt;has&lt;/strong&gt; the ability to move, and to take damage. It’s a small change in wording, but a totally different concept. Let’s try to redefine our &lt;code&gt;Moveable&lt;/code&gt; and &lt;code&gt;TakesDamage&lt;/code&gt; classes, to better suit composition.&lt;/p&gt;
&lt;p&gt;First, we need to define some utility types:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;abstract&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ComponentFunction&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ComponentClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (...&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With composition, we need to distinguish between the composable objects, and the composed objects. In our case, &lt;code&gt;Moveable&lt;/code&gt; and &lt;code&gt;TakesDamage&lt;/code&gt; are reusable classes that have no meaning outside a context of a game object. We can create an instance of &lt;code&gt;Moveable&lt;/code&gt; class, but it has no meaning by itself. Therefor, we will separate those containers of common logic, into what I’ll call a &lt;code&gt;Component&lt;/code&gt;. Hence, the abstract &lt;code&gt;Component&lt;/code&gt; class. The two types are used for some type inference, and their role will be clear in a bit.&lt;/p&gt;
&lt;p&gt;Next, let’s rewrite our &lt;code&gt;Moveable&lt;/code&gt; and &lt;code&gt;TakesDamage&lt;/code&gt; as components that will be composed, and at the same time, rename &lt;code&gt;TakesDamage&lt;/code&gt; to &lt;code&gt;Health&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; MoveableComponent&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; position&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B;font-style:italic&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; moveTo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; pos&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; HealthComponent&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt; health&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B;font-style:italic&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; takeDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; isDead&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;health&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we marked some classes as components. Before we can finally define our &lt;code&gt;EvilOrc&lt;/code&gt; class, we need one more utility class:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;abstract&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; GameObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  private&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; components&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ComponentFunction&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  protected&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; addComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  hasComponents&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Iterable&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ComponentFunction&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; components&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;has&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;componentClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ComponentClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;componentClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This class will serve as the base class for all our game objects, and it has a list of components. Hence, composing them into a game object. Here, the types &lt;code&gt;ComponentFunction&lt;/code&gt; and &lt;code&gt;ComponentClass&lt;/code&gt; are used for some Typescript magic.&lt;/p&gt;
&lt;p&gt;Lastly, we need to define our &lt;code&gt;EvilOrc&lt;/code&gt; and &lt;code&gt;MimicChest&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; EvilOrc&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; GameObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;initialPosition&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Vector2D&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B;font-style:italic&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;addComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; MoveableComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;initialPosition&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;addComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; HealthComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; MimicChest&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; GameObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B;font-style:italic&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;addComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; HealthComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;initialHealth&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And voilà! As I’ve said earlier: &lt;code&gt;EvilOrc&lt;/code&gt; is not &lt;strong&gt;a&lt;/strong&gt; &lt;code&gt;Moveable&lt;/code&gt; and &lt;code&gt;Health&lt;/code&gt; object. Instead, &lt;code&gt;EvilOrc&lt;/code&gt; &lt;strong&gt;has&lt;/strong&gt; the ability to move, and &lt;strong&gt;has&lt;/strong&gt; health (and hence—ability to take damage, in our case).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Okay&lt;/em&gt;—you say. &lt;em&gt;But what do I do with this? How do I operate on this?&lt;/em&gt; Let’s say we are trying to build a sequence that will perform an attack by the player, on a NPC. This might look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; performAttack&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;go&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;GameObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;go&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;hasComponents&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;HealthComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;])) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;game object does not have health component&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; h&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; g&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;HealthComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;isDead&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;game object already dead&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  h&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;takeDamage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;damage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a very modular system that allows you to compose all kinds of behavior. It’s widely used in game development, and is knows as ECS—Entity Component System. I was never able to find use-cases for it outside of game development, but recently I had the chance to use a similar implementation at my work. I thought it’s worth sharing this with you, and maybe you can find a good use cases for it as well.&lt;/p&gt;
&lt;p&gt;Imagine for example that you want to build a highly reliable weather service. It has to depend on multiple external weather APIs, because you want to fallback to a different API if one is down, or takes too much time to answer. But not all weather APIs might support the features you need. One might support temperature and wind conditions; another one will support temperature and precipitation; while the last might support all of them: temperature, wind condition, and precipitation. This example might be a prefect use case for the aforementioned composition. The implementation of such system, using composition—will be clean and easy to maintain.&lt;/p&gt;
&lt;p&gt;Remember—good developers have a lot of tools, and try to use the best one for each scenario. Happy coding!&lt;/p&gt;</content:encoded></item><item><title>How to get into programming in 2023</title><link>https://kudmitry.com/articles/how-to-get-into-programming-in-2023/</link><guid isPermaLink="true">https://kudmitry.com/articles/how-to-get-into-programming-in-2023/</guid><pubDate>Thu, 05 Jan 2023 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s 2023 already, you’ve been a responsible individual and created a new year’s resolution. One of the items in your resolution is a career change to become a software engineer. So how do you start?&lt;/p&gt;
&lt;p&gt;Becoming a software engineer is 2023, is no different than in 2022, or 2021. But for some unexplainable reason—new year is considered to be a good reset point when we decide to pursue new endeavors or set goals. It doesn’t matter if you are fresh out of school, or looking for a career change after many years in a particular industry—becoming a software engineer is a great way to up your skills as well as pursue a new career.&lt;/p&gt;
&lt;p&gt;Let this be your guide on how to make your first steps towards your desired change. I’m not going to teach you programming, or telling you what path to pursue among the many. Instead, I just want to give you my personal recommendations on what to focus on during your learning.&lt;/p&gt;
&lt;h2 id=&quot;aint-programming-dead&quot;&gt;Ain’t programming dead?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Haven’t the demand for programmers declined rapidly?&lt;/em&gt; &lt;em&gt;Aren’t the AI going to replace programmers?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;No, and no. The end of 2022 was rough on the industry, with massive layoffs in many companies. But developers are still needed. As our world becomes more and more dependent on software, the need for good software engineers will persist. And as for AI, I’ve &lt;a class=&quot;internal new&quot; href=&quot;/articles/chatgpt-ai-and-the-future-of-tech/&quot;&gt;blogged earlier&lt;/a&gt; why I don’t think ChatGPT will replace developers any time soon.&lt;/p&gt;
&lt;p&gt;Now that we’ve cleared out those two misconceptions, let’s discuss all the possible paths you can take, in no particular order.&lt;/p&gt;
&lt;h2 id=&quot;the-gamer&quot;&gt;The gamer&lt;/h2&gt;
&lt;p&gt;So you want to be a game developer? This is a very popular and demanding path. Many newcomers to this profession want to build games. In fact, the first serious things I’ve built when I was learning C in school—are games. Conway’s game of life and snake, to be exact.&lt;/p&gt;
&lt;p&gt;Game development is a very broad umbrella term. It covers many disciplines such as: audio programming, graphics programming, physics programming, AI, game logic scripting, etc. Each of these disciplines can be their own career path. I’d say that graphics, AI, and game logic are the most interesting, but you might have interest for other things.&lt;/p&gt;
&lt;p&gt;However, if you really want to become a game developer, I would suggest you focus on building games. Many beginning game developers are getting caught in the low level stuff. How to build a game engine, how to draw triangles, etc. And while this is fun, it does not bring you closer to actually building games. The ability to finish and ship a product is more important than building a game engine, which chances are you won’t be doing in your first years of this career path anyway.&lt;/p&gt;
&lt;p&gt;The easiest, and fastest way to build games today—is to use a ready engine such as &lt;a href=&quot;https://unity.com/&quot;&gt;Unity&lt;/a&gt;. You will be able to utilize the amazing building blocks provided to you by Unity, while focusing on building your game. Development in Unity is done mainly in &lt;strong&gt;C#&lt;/strong&gt;, which is a good overall programming language to learn. Unity can be used to build desktop and mobile games, and many AAA titles are built with Unity (I’m not an avid gamer, but Pillars of Eternity is one that comes to mind).&lt;/p&gt;
&lt;p&gt;However, even though Unity abstracts a lot of the basics of graphics, audio, physics, and AI concepts—you will still need to know and understand them. And most of them are based on math and algorithms. Ray casting, path finding, vectors, quaternions. All these words will need to become your best friends. Also keep in mind that the game development industry is known to be a tough path. Crunching is real, and according to international game developers’ association survey, done in 2019, 41% of respondents indicated that their job involved crunch time, with 38% indicated working between 50 and 59 extra hours per week &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-mobile-developer&quot;&gt;The mobile developer&lt;/h2&gt;
&lt;p&gt;Smartphone took over the world. It became the main device through which we consume media. Even my blog was visited primarily (51%) via mobile device, in the past 12 months. And so becoming a mobile developer, could be a viable career path to choose.&lt;/p&gt;
&lt;p&gt;In order to get into mobile development, you need to decide what platform you want to start with: Apple iOS or Google Android. While emulation software exists, it’s always better to test on a live device, and so my recommendation would be to go with the one you already own. Keep in mind that with Apple it’s a bit more problematic as you need access to macOS in order to develop for the iPhone.&lt;/p&gt;
&lt;p&gt;As for languages, the choice is very limited. If you plan to develop iOS applications (or any applications for the Apple ecosystem, for that matter), your two choices are &lt;strong&gt;Objective-C&lt;/strong&gt; and &lt;strong&gt;Swift&lt;/strong&gt;. Objective-C is old and somewhat outdated. Swift is a way nicer language, according to Apple it’s faster than Objective-C, and on top of that it can be used with SwiftUI, which is a modern way to declare user interfaces. For Android, on the other hand, &lt;strong&gt;Java&lt;/strong&gt; and &lt;strong&gt;Kotlin&lt;/strong&gt; are your only options. Java is much more verbose than Kotlin, and I, personally, prefer Kotlin. Both of them are good languages to know, as they can be used in many industries.&lt;/p&gt;
&lt;p&gt;However, as smartphones become more and more important, and companies are looking to save cost on developers, another viable option could be—write once, run everywhere approach. In this approach, you develop one code base which is then compiled to run on both iOS and Android. The two main frameworks for that are React Native developed by Facebook, and Flutter developed by Google. React Native uses &lt;strong&gt;JavaScript&lt;/strong&gt;, an insanely popular language (more on that later), while Flutter uses &lt;strong&gt;Dart&lt;/strong&gt;—a somewhat niche language developed by Google.&lt;/p&gt;
&lt;p&gt;Flutter should be more performant than React Native, but Dart is not that popular. So it’s up to you to do the research on what technology is demanded in your area. Keep in mind that you will probably compete with the cool kids (read about them in the next section) who are doing web development. Having a mobile application is a must for a successful product, but many companies will cheap out on proper mobile developers, and instead will repurpose a good frontend engineer, who knows JavaScript, to do a semi-native development using React Native.&lt;/p&gt;
&lt;h2 id=&quot;the-cool-kid&quot;&gt;The cool kid&lt;/h2&gt;
&lt;p&gt;The most popular path to choose in 2023—is no doubt &lt;strong&gt;web development&lt;/strong&gt;. If smartphones became our main devices of choice, then the web became our main operating system. I bet most computers today run mainly Chrome or any other browser, and all our interactions are done through the web. The browser can run video conferencing, graphic design, and even gaming. There are two main careers you can take there. Frontend development, and backend development.&lt;/p&gt;
&lt;p&gt;Frontend development focuses mainly on what we see. When you go to YouTube and interact with the video, you are interacting with the works of a frontend engineer. For years, frontend was relatively simple. But recently it got a lot more challenging, at least as challenging as backend, if not more challenging. The most popular and recommended way to get into frontend development—is the holy trifecta of &lt;strong&gt;HTML&lt;/strong&gt;, &lt;strong&gt;CSS&lt;/strong&gt;, and &lt;strong&gt;JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once you know how to build a web page using HTML, how to style it using CSS, and how to make it interactive using JavaScript, the next step would be to pick up a JavaScript framework. React is by far the most popular JavaScript frontend framework. Others are Vue and Svelte, so I recommend you to check if they are more popular in your region, and maybe pick one of them instead.&lt;/p&gt;
&lt;p&gt;It is also possible to become a backend developer. Backend developers are the ones responsible for feeding the data to and from the frontend. In the example of YouTube, backend development might include things like storing the videos, processing them, streaming them into the browser, authenticating users, etc. The most popular framework for building backend applications is NodeJS, which is written in JavaScript. So if you want to be a backend developer, I suggest you learn &lt;strong&gt;JavaScript&lt;/strong&gt; and then pick up NodeJS. To further improve your employability, you should definitely know how to work with Databases such as PostgreSQL and or Mongo.&lt;/p&gt;
&lt;p&gt;However, it is possible to write backend in other languages such as Ruby, Python, or PHP. If you do decide to chose one of those, I’d recommend you go with Python as it is a more versatile language that can be used in different fields like Machine Learning or Data Analytics. If you plan to work primarily with WordPress, then PHP is the obvious choice. Ruby is still used in some companies, but it’s slowly phasing out, and finding Ruby developers becomes harder and harder. This can be an opportunity for you, if your city or country has demand for Ruby developers. And according to the latest StackOverflow developer survey, Ruby is one of the top paying languages. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Web development is by far the most popular route as of today. It has the biggest market, and plenty of learning materials. On the other side, it creates a strong competition as everyone is trying to become a web developer. And you always need to be one step ahead of everyone else in order to raise your chances of success.&lt;/p&gt;
&lt;h2 id=&quot;everything-else&quot;&gt;Everything else&lt;/h2&gt;
&lt;p&gt;The above careers paths are not the only ones you can take. You can decide to go the route of data analytics or machine learning, where &lt;strong&gt;Python&lt;/strong&gt; is dominating. If you like servers and infrastructure, you can choose the path of a DevOps engineer, where languages like Shell scripting, Ruby, and Python as well as tools like Chef, Ansible, Terraform, Docker, and Kubernetes are the most demanded.&lt;/p&gt;
&lt;p&gt;Desktop application development, while slowly dying, is still needed for some industries. There, like with mobile development, a lot of the decision depends on the operating system you are planning to develop for. For macOS, as with iOS development, Swift is the de facto standard. For Windows, you can use C# or C++. For Linux, it’s primarily C++. And as with mobile, you can also do cross-platform development with Java/Kotlin, C++ and a framework like Qt, Flutter and Dart, or Electron and JavaScript.&lt;/p&gt;
&lt;p&gt;Aside from all that, system development is a viable career path as well. The world runs on operating systems and networking equipment, and someone has to write drivers for those. More over, the market for Internet-of-Things (IoT in short) is rapidly growing, with devices like smart home appliances, or embedded systems that run on a SoC like Raspberry Pi. As it’s the closest you can get to hardware, and performance matters a lot, development in this area is done mainly in C and recently Rust. If you do decide to go that route, keep in mind that you need to understand how hardware works and be prepared to work with manual memory management, as well as understand how to write and debug low level code.&lt;/p&gt;
&lt;h2 id=&quot;beyond-the-basics&quot;&gt;Beyond the basics&lt;/h2&gt;
&lt;p&gt;A bit more than twenty years ago, the world was switching from Windows 95 to Windows XP, and the internet was growing in popularity. At that time, a career in software engineering wasn’t something as common as today. I bet that today it’s one of the most popular choices for a career. Our increased dependency on technology created the increased demand for software engineers. The more people take this career path, the harder it will be to get there. And the only way to increase your chances—is to be &lt;strong&gt;curious&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A specific language or framework might help you land your first job, but vast knowledge in how computers work—will help you to build a career. Software engineering is fun. It’s well paid. But it’s &lt;strong&gt;not easy&lt;/strong&gt;. And it will be harder each year, as more people decide to choose this path. If you do it to escape your soul sucking career, without any passion or desire to learn and stay curious—you will eventually find your self in the same soul sucking career you wanted to escape from. However, if you like to solve problems, passionate about learning and exploring new topics—welcome aboard! And enjoy your stay there!&lt;/p&gt;
&lt;p&gt;Happy 2023!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://s3-us-east-2.amazonaws.com/igda-website/wp-content/uploads/2020/01/29093706/IGDA-DSS-2019_Summary-Report_Nov-20-2019.pdf&quot;&gt;IGDA Developer Satisfaction Survey 2019&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://survey.stackoverflow.co/2022/#technology-top-paying-technologies&quot;&gt;Top paying technologies - StackOverflow developer survey 2022&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>The missing ingredients from your monitoring alerts</title><link>https://kudmitry.com/articles/the-missing-ingredients-from-your-monitoring-alerts/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-missing-ingredients-from-your-monitoring-alerts/</guid><pubDate>Thu, 22 Dec 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There is one thing engineers hate the most—waking up at night and troubleshooting production incidents. What if I told you I know how to make it less painful?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.honeycomb.io/blog/devops-on-call&quot;&gt;Nobody wants to be on–call&lt;/a&gt;. Back in the old days of desktop applications, the on–call duty was reserved mainly to network and infrastructure engineers, who were responsible to make the internet work at all times. In today’s world, where every single app—is a SaaS served via the only operating system you will ever need, the browser—on–call duty has been propagated towards software engineers as well.&lt;/p&gt;
&lt;p&gt;It doesn’t matter if you are a frontend or a backend engineer, if you are junior or senior—chances are you will, eventually, find yourself as part of an on–call rotation.&lt;/p&gt;
&lt;h2 id=&quot;what-it-means-to-be-paged&quot;&gt;What it means to be paged?&lt;/h2&gt;
&lt;p&gt;Before I move on to your broken alerts, let’s set the record clear on the meaning of being paged. Paging an engineer means that your system performance has been degraded. This might mean a total degradation—like inability of users to access your system, or it might mean partial degradation—like inability to send registration emails.&lt;/p&gt;
&lt;p&gt;From a business perspective, this usually means that your business is losing money. Every time your customers are experiencing service degradation, they will demand compensation in accordance with your SLA. Inability to acquire new customers, means they will switch to a different service—hence depriving you of their money.&lt;/p&gt;
&lt;p&gt;From an engineering perspective, this means an immense pressure. During an on–call page, the number one priority of the paged engineer is to get the system back and running &lt;strong&gt;as fast as possible&lt;/strong&gt;. This is not a time for &lt;em&gt;nice&lt;/em&gt; solutions, or a major refactor. This is a time when you need to move fast, and with confidence. Keep that in mind, we will circle back to this later.&lt;/p&gt;
&lt;h2 id=&quot;why-you-are-being-paged&quot;&gt;Why you are being paged?&lt;/h2&gt;
&lt;p&gt;If we look at all the reasons for paging an on–call engineer, we can group them in two main categories: &lt;strong&gt;manual&lt;/strong&gt; and &lt;strong&gt;automatic&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Manual paging is self-explanatory. Your customer calls the customer support and notifies of a problem she is having. The customer support representative usually goes through a series of predefined steps, as he lacks the technical knowledge of the internals of the system, to either mitigate, or escalate the issue. After failing to mitigate the issue, and upon realization that the problem is critical, he might decide to page the on–call engineer. Depending on the nature of the organization, the customer support rep might page a SOC engineer, who will in turn might escalate to either infrastructure or a software engineer. In such organizations, software engineers are typically the last in line to be paged. In a smaller, less defined organizations, customer support might page the engineer directly.&lt;/p&gt;
&lt;p&gt;With manual paging, there is not a lot to do. Each case is usually unique and requires a thorough investigation by the paged engineer to mitigate the issue. What can be improved, however—are automatic pages.&lt;/p&gt;
&lt;p&gt;Automatic paging is triggered by your monitoring system. With proper monitoring in–place, you can define alerts that will be triggered upon different events. For example: an alert that triggers when the amount of free disk spaces reaches below certain point, indicating a potential failure due to inability to store data. Or an alert that monitors the RAM usage of a machine and triggers at a specific threshold, which might indicate a potential memory leak which will lead to an out of memory exception, and eventually—a painful death of your process by the operating system.&lt;/p&gt;
&lt;p&gt;And from my experience, automatic paging is where most people, and alerting systems—get it wrong.&lt;/p&gt;
&lt;h2 id=&quot;the-missing-ingredients-from-your-monitoring-alerts&quot;&gt;The missing ingredients from your monitoring alerts&lt;/h2&gt;
&lt;p&gt;During an on–call trigger, the paged engineer experiences a context switch. Mental context switch, just like the context switch done by OS schedulers, is an extremely expensive and &lt;a class=&quot;internal new&quot; href=&quot;/articles/context-switch-the-only-operation-that-kills-productivity/&quot;&gt;productivity taxing&lt;/a&gt; operation for the human brain. Combined with the fact that from the moment you’ve been paged—your company starts to, potentially, lose money—it’s a terrible situation to be in.&lt;/p&gt;
&lt;p&gt;The last thing you want to do under such circumstances—is to start understanding what happened. At the moment you’ve been paged, you already should know what’s the issue. And so, the first missing ingredient is—clarity.&lt;/p&gt;
&lt;h3 id=&quot;have-clarity-in-your-alerts&quot;&gt;Have clarity in your alerts&lt;/h3&gt;
&lt;p&gt;Imagine the following PagerDuty alert: &lt;em&gt;Database disk reached 90% usage&lt;/em&gt;. How clear is this alert? The only information I can gather from the alert itself—is that disk space is running out. Here are some of the questions that I have no answers for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What database is it? (I could have multiple)&lt;/li&gt;
&lt;li&gt;What region? (I could have multiple)&lt;/li&gt;
&lt;li&gt;How fast is it filling up?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By getting the answers to the first 2 questions, I eliminate the unnecessary time spent by browsing the terrible AWS console for all my databases, in an attempt to find the affected one.&lt;/p&gt;
&lt;p&gt;By knowing the rate of filling, I know what are my time constraints. If I’m going to run out of space in 7 days, maybe I should not be paged at 2AM. However, if there is an automatic process that got stuck, and in about 1 hours I’m going to run out of space—I better hurry up before my entire database crashes.&lt;/p&gt;
&lt;p&gt;A good alert should have clarity. It should point you to the alerting resource, give you time constraints, and if possible direct you towards the offending service or sub system. For example: &lt;em&gt;[us-east-1] Database &lt;code&gt;pikachu&lt;/code&gt; is filling at a rate of 1 GB per hour and will run out of space in ~1 hour&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I know that I have 1 hour max. I know the exact resource and where it’s located. This automatically might help me identify the potential issue, without even looking at the code. I might remember, out of the blue, the Jake pushed a new cron earlier today, that writes to this database. And so this might be a potential vector of investigation. This gives me a starting point, as opposed to blindly browsing AWS for all my databases, in all my regions, to find the offending database, while wasting my sleep hours as well as precious time I’ve been given to resolve the issue.&lt;/p&gt;
&lt;p&gt;However, I still lack one major ingredient.&lt;/p&gt;
&lt;h3 id=&quot;a-checklist&quot;&gt;A checklist&lt;/h3&gt;
&lt;p&gt;Aviation is one of the safest forms of transportation. There are 3 ingredients that make it safe:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Well trained pilots&lt;/li&gt;
&lt;li&gt;A lot of automation&lt;/li&gt;
&lt;li&gt;A checklist&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’ve ever looked out of the window of an airplane, just before takeoff, you probably noticed that all the flaps are suddenly going up and down, back and forth. The reason for that—pilots are running a pre–takeoff checklist. It doesn’t matter if you are a novice pilot, or a veteran one, before every takeoff—you run a checklist to check the critical systems of the airplane.&lt;/p&gt;
&lt;p&gt;For some mysterious reason, we, engineers—don’t do that. Imagine the following alert: &lt;em&gt;Subsequent requests to service &lt;code&gt;xyz&lt;/code&gt; failed continuously for the past 15 minutes&lt;/em&gt;. This alert monitors the status of some &lt;code&gt;xyz&lt;/code&gt; service, by issuing requests to it, and now indicate that those requests have failed. For the sake of example, let’s assume that &lt;code&gt;xyz&lt;/code&gt; is an &lt;strong&gt;external&lt;/strong&gt; service you depend on. It could be an email provider, or an API that gives you weather information.&lt;/p&gt;
&lt;p&gt;One potential approach would be to go and start looking at the logs. Chances are, if this alert is infrequent, you won’t remember the exact log index or query you need to execute. And so you will try to fish the right query, to understand why requests to this service—fail. After eventually finding the correct log entry, and seeing that the error code is 503, you now start to defragment your fragile memory, and the neurons in your brain are starting to catch fire—while you try to remember who is the relevant person to reach out to in order to verify whether &lt;code&gt;xyz&lt;/code&gt; is not having any issues on their side. After aging faster than you should have, you open a support ticket to &lt;code&gt;xyz&lt;/code&gt; customer support, and they notify you—&lt;em&gt;yeah, we have some issues on our side&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Humans are imperfect. And even though we—engineers, work with the same system every day, just like pilots who fly every day, we might make mistakes, just like pilots. But pilots have checklists. And instead of remembering a bunch of pre-takeoff procedures, they just need to remember one—run the damn checklist!&lt;/p&gt;
&lt;p&gt;This entire issue could have been resolved if we had a checklist like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify the error code returned from &lt;code&gt;xyz&lt;/code&gt; by following [this link] to the logs and looking for the field &lt;code&gt;response_code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the code is &lt;code&gt;503&lt;/code&gt;—open a support ticket, using [this form] and verify whether &lt;code&gt;xyz&lt;/code&gt; is having problems on their side&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Not only this guides you step–by–step on how to identify, and possibly resolve the issue—it also functions as future documentation for new engineers that enter your on–call schedule.&lt;/p&gt;
&lt;p&gt;I don’t know the best way to manage those checklists. I tend to hate Wiki pages as they become a pile of unsearchable garbage, but if you are able to keep a clean Wiki—it could work for you. You can have it pinned in your teams’ Slack channel. If you have other creative solutions, please feel free to share them with me (contact details are at the bottom of this post).&lt;/p&gt;
&lt;p&gt;The best place would be to have it inside the alert itself. Once I get paged, I just go to the alert and can see the checklist there. Moreover, I can edit and fine tune the alert checklist as I go, or after I’ve finished with the incident. I was surprised that no monitoring system offers such capabilities.&lt;/p&gt;
&lt;p&gt;Remember—incidents and pagers are stressful events, and people are imperfect. We tend to forget how things work, even if we work with them on a daily basis, and especially under stress. By eliminating as much decision–making during the incidents, as possible, we can create a better experience and faster resolution of production incidents.&lt;/p&gt;
&lt;p&gt;I use checklists a lot in my life. This blog post, and all others I write, follow an always–evolving checklist during all the publication stages. If you are interested to know how I manage my writing—drop me a comment in one of the contact channels below. Until next time.&lt;/p&gt;</content:encoded></item><item><title>Living side-by-side with an AI</title><link>https://kudmitry.com/articles/living-side-by-side-with-an-ai/</link><guid isPermaLink="true">https://kudmitry.com/articles/living-side-by-side-with-an-ai/</guid><pubDate>Wed, 14 Dec 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2022 is, undoubtedly, the year of the AI. It started with DALL-E, then GitHub Copilot, and now—ChatGPT. AI is here to stay, whether you like it or not, and we ought to find a way to live with it.&lt;/p&gt;
&lt;p&gt;Earlier this month, the OpenAI team released &lt;a href=&quot;https://chat.openai.com/&quot;&gt;ChatGPT&lt;/a&gt;—a GPT-3 based AI chat assistant that can do a lot of things like drafting a blog post idea, writing essays, explaining various topics, writing code etc. I’ve shared my initial impression about it in my previous blog post—&lt;a class=&quot;internal new&quot; href=&quot;/articles/chatgpt-ai-and-the-future-of-tech/&quot;&gt;ChatGPT, AI, and the future of tech&lt;/a&gt;. In the past week, I’ve used ChatGPT to perform some mundane tasks, which I usually use Google for. In this post, I want to raise some questions on how we, humans, are going to co-exist with AI.&lt;/p&gt;
&lt;h2 id=&quot;immediate-impact-of-chatgpt&quot;&gt;Immediate impact of ChatGPT&lt;/h2&gt;
&lt;p&gt;In my previous post, I said that I don’t think software engineers or content creators will lose their jobs, at least not in the near future. I still stand behind this opinion. However, as with any technological progress, there are those who are going to be impacted—for good and for bad.&lt;/p&gt;
&lt;p&gt;When the assembly line was introduced, and later on—perfected with automation, people who used to do what robots do now—lost their jobs. That’s the price we pay for technological advancement. Mundane, and repetitive jobs—are always the first in–line to be replaced. Things like low quality content production for SEO purposes, or researching keywords for blog posts. All these can be easier done by ChatGPT with minor corrections from a human expert.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/chatgpt-keywords-for-blog-post_t5obtt.webp&quot; alt=&quot;ChatGPT generates me some keywords for a blog post#859x728&quot;&gt;&lt;figcaption&gt;ChatGPT generates me some keywords for a blog post#859x728&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Yet, there are always people who look for low-hanging fruits. In today’s world—content is highly rewarded. No matter what niche you take, be it software engineering or self development, the successful people in that niche produce content. Blog posts, podcasts, YouTube videos, books, courses etc. You can say we do it for ourselves, but the truth is—we all, including myself, want to share our content with the world. Those who write for themselves—keep a journal, not a blog. We all do it for different reasons. Some to learn, others to teach. Some to share with others, others to enrich themselves. I’m not the one to judge, and I believe people should be paid for their knowledge—if they desire so.&lt;/p&gt;
&lt;p&gt;However, the thing with new content producers is they think it’s easy. Just slap in some words in a text editor and publish it to WordPress, or film yourself talking to a camera and upload to YouTube. But in reality—content production is a hard job that involves not only the ability to write or speak, but also the ability to distribute your content and build an audience.&lt;/p&gt;
&lt;p&gt;And while most of the creators take the hard route of producing content, there are people who will always try to find shortcuts. Like with money making—some people go to work every day, while others try to set up a &lt;em&gt;get-rich-fast&lt;/em&gt; scams. And the big beneficiaries of AI technologies today—are those who are looking for shortcuts.&lt;/p&gt;
&lt;p&gt;I’m relatively active in communities like HackerNews and Reddit, and I can see the explosion of ChatGPT content on those platforms. I even had an answer to one of my questions, copy–pasted from ChatGPT. Granted, the author of the answer disclosed he/she used ChatGPT. &lt;a href=&quot;https://www.notion.so/&quot;&gt;Notion&lt;/a&gt;, the highly popular note–taking app, that I no longer use, recently released &lt;a href=&quot;https://www.notion.so/product/ai&quot;&gt;Notion AI&lt;/a&gt;—an ability to generate content right inside the editor itself, leveraging GPT-3 model. They did not work on the countless issues that their users have with the software, such as lack of offline support or E2EE—instead, they’ve put their efforts into AI.&lt;/p&gt;
&lt;p&gt;And it’s a smart move because I believe there will be a huge demand for AI, due to the important role of content creation in our current society. Websites, like StackOverflow, have now implemented a &lt;a href=&quot;https://meta.stackoverflow.com/questions/421831/temporary-policy-chatgpt-is-banned&quot;&gt;temporary policy&lt;/a&gt; to ban ChatGPT generated content. Getty &lt;a href=&quot;https://www.theverge.com/2022/9/21/23364696/getty-images-ai-ban-generated-artwork-illustration-copyright&quot;&gt;bans&lt;/a&gt; AI generated content. And I’ve read somewhere that Google is working on, or already have, a policy to punish websites that use primarily AI generated content. I can’t find the source, so don’t quote me on that.&lt;/p&gt;
&lt;h3 id=&quot;the-human-touch&quot;&gt;The human touch&lt;/h3&gt;
&lt;p&gt;From all the examples I’ve seen, and based on my personal experience—ChatGPT is often times wrong. It gets a lot of the things right, but usually make a mistake here and there. And this is one of the dangers of using its raw output. A person, who has no understanding of the topic, can not effectively use ChatGPT. I can ask ChatGPT how a nuclear bomb works, but I’m not a nuclear scientist to verify that the answer is correct. I can, however, go on Quora or Reddit and spam answers at a rate of one per minute, hoping that 90% of them will be correct—while ripping the benefits.&lt;/p&gt;
&lt;p&gt;The power of communities like StackOverflow or Reddit—is the positive feedback loop we get from our fellow humans. The ability to audit a solution, or provide multiple answers from multiple people—is priceless in the world of self development and knowledge acquiring. People make mistakes—that’s why we have protocols in place, as well as fellow human beings, to correct our mistakes. ChatGPT is free, for now. If I want to read AI generated content, I can use ChatGPT directly. And so, flooding the internet or the above communities with AI generated content, makes it harder to maintain the quality of the content as well as getting &lt;strong&gt;real&lt;/strong&gt; human answers that come from their personal experience and knowledge.&lt;/p&gt;
&lt;h2 id=&quot;what-is-creativity&quot;&gt;What is creativity?&lt;/h2&gt;
&lt;p&gt;When we talk about AI generated content, we need to talk about creativity. If we put aside for a moment, the fact that ChatGPT produces inaccurate content in most cases, one could ask then—&lt;em&gt;what’s the difference between a content written by a human and a content created by a machine?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Creative process is not something that can be described mathematically. Humans, usually, describe their creative process as &lt;em&gt;a-ha!&lt;/em&gt; moments. Something like: &lt;em&gt;I was taking a shower, while thinking about the solution to that problem, and then it hit me! I need to use &lt;a href=&quot;/tag/javascript&quot;&gt;JavaScript&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We then go on and do research on that topic, by reading materials, and eventually summarizing the entire process in a unit of production—be it a system design, a piece of code or art. If we look beyond technical fields, and gravitate towards more creative fields like writing or singing—we can learn that many writers or poets produce content based on their personal experience and background. A complicated child might become a rapper who will rap about his though childhood, which might resonate with children in a similar situation. A WW1 veteran—might write about his horrible experience during the war, as a way to ease his pain and help him cope with that experience, while passing onto the reader the horrors of those events.&lt;/p&gt;
&lt;p&gt;The word creativity in itself is associated with something human. AI has no experience. It has no notion of past. It’s not having childhood traumas. Nor it can think like a human can. Therefor, I believe it has no creativity—it can only remaster. And there is something humanly when we look at art or read books and can &lt;em&gt;feel&lt;/em&gt; the same things the artist or the author were feeling. I can’t have sympathy towards machine. I usually kick my robot vacuum cleaner when it’s stuck. &lt;em&gt;Please don’t call the robots abuse hot line&lt;/em&gt;.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/chatgpt-on-creativity_rzjz94.webp&quot; alt=&quot;ChatGPT talks about creativity#878x680&quot;&gt;&lt;figcaption&gt;ChatGPT talks about creativity#878x680&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;something-from-nothing&quot;&gt;Something from nothing&lt;/h3&gt;
&lt;p&gt;One could, rightfully, ask—&lt;em&gt;Humans rarely create anything from nothing. How is that different from an AI?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I do want to point out that &lt;strong&gt;there are&lt;/strong&gt; great discoveries made by humans. My point is not to say that all we create—is a remix of existing things. But as I’ve said earlier, most of what we do create—is based on past knowledge and experience. A student of Rembrandt will be inclined to create art in a similar style to his teacher—unless he or she decides to explore other styles or artists. The same goes to all other types of content creation. What I write—is based on what I read and the experience I have.&lt;/p&gt;
&lt;p&gt;The difference between me and AI—is that I have context and understanding. I know that &lt;code&gt;2+2=4&lt;/code&gt;. A good mathematician, which I am not, also knows &lt;em&gt;why&lt;/em&gt;. AI—does not. It simply fed this fact into its neural network. If you feed it with another &lt;em&gt;fact&lt;/em&gt;, it will produce answers based on that fact. This creates a negative feedback loop. I’ll talk a bit later about the negative feedback loop.&lt;/p&gt;
&lt;h3 id=&quot;what-is-copyright&quot;&gt;What is copyright?&lt;/h3&gt;
&lt;p&gt;When we talk about creativity and remastering—we have to talk about copyright. As I’ve said in the beginning of this post—2022 is the year of the AI. During a very short period of time, something like 3–4 months, we’ve gone from &lt;em&gt;closed-behind-doors-deep-learning-ai-that-beats-humans-in-go-or-chess&lt;/em&gt;, towards AI that generates art, code, and poems.&lt;/p&gt;
&lt;p&gt;But you know what’s missing? AI that generate music or videos. Yes, there is &lt;a href=&quot;https://openai.com/blog/jukebox/&quot;&gt;OpenAI Jukebox&lt;/a&gt;, but it didn’t get as much attention as DALL-E, Stable Diffusion, or ChatGPT. While the results of it are amazing—they are not on the same level as other AI tools. And there is no, yet, AI that can generate video content such as movies. I think it’s related to the fact, that existing AI models are trained on existing data. Access to royalty free music or films—is limited. GitHub copilot was trained on public GitHub repositories, while ChatGPT was trained on crawling the internet. Those are huge datasets. GitHub has nearly 128 million of public repositories; while the GPT-3 model was trained on nearly 400 billion websites.&lt;/p&gt;
&lt;p&gt;I don’t believe knowledge or art should be safeguarded, unless the author decides so. But I do believe that people have the right to retain copyrights for their work. I’m not a copyright lawyer, so I don’t know exactly where is the line between original work and remixed work. But I know for sure that I can not publish a YouTube video with Britney Spears song in it. This video will be removed, for respect to Britney’s copyright. The same respect that other authors, or open source developers—have. Is it right to train an AI model on an open source project, just because it’s open source? Is it right to train an AI model on a website, just because it’s publicly available? Is it moral to charge money for &lt;a href=&quot;https://apps.apple.com/us/app/lensa-ai-photo-video-editor/id1436732536&quot;&gt;an app&lt;/a&gt; that uses an AI model, which is, most likely, trained on a publicly available art, that generates avatars for you—without giving credit or attribution back to the artists that “inspired” the AI model?&lt;/p&gt;
&lt;p&gt;I don’t know. I know there is a &lt;a href=&quot;https://githubcopilotlitigation.com/&quot;&gt;lawsuit against copilot&lt;/a&gt;. And I know that we &lt;strong&gt;must&lt;/strong&gt; have a discussion as to how we should train those AI models. The fact that some datasets used to train the models—are public, does not mean it can be used without implications, credits, or attributions. The fact that one owns a bunch of legally obtained DVDs with movies, does not grant them the right to open a private movie theater. Images, articles, and blogs posts—sometimes have licenses. And those licenses should be respected by humans as well as AI. And unless we start to raise awareness for this issue, and have an open discussion—we are, potentially, violating the rights of content producers and artists.&lt;/p&gt;
&lt;h2 id=&quot;negative-feedback-loop&quot;&gt;Negative Feedback Loop&lt;/h2&gt;
&lt;p&gt;I’ve mentioned it earlier, and I want to expand on this topic. In one of my previous posts, &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-software-industry-is-broken/&quot;&gt;The software industry is broken&lt;/a&gt;, I’ve discussed the fact that software today—is not as optimized as it used to be. One of the reasons I’ve outlined—is the fact that writing software became easier, and in a way &lt;em&gt;dumber&lt;/em&gt;. Developers today don’t need to know how memory is managed. There are high level languages that manage it for you. And so, as tools become more available to the masses—they’re ought to be simplified. And simplification, often times, leads to loss or deterioration of knowledge.&lt;/p&gt;
&lt;p&gt;A similar thing can, and will, happen to our knowledge if we do not control the spread of AI content. AI is great today because of two reasons—(1) the engineers behind it and (2) the entire knowledge of human beings. Every time an AI generates a piece of code, art, or a text paragraph—it’s because there is work made or knowledge shared by humans, that the AI was able to train on.&lt;/p&gt;
&lt;p&gt;I’ve said earlier that, as of today, ChatGPT gets most of the things right, and some things wrong. If we don’t set up protocols in place to distinguish human generated content from AI generated content, AI generated content will flood the internet. Look at how the standards of beauty have changed, because everyone is posting unrealistic photos of themselves, by using a bunch of photo retouching, auto-magical, tools.&lt;/p&gt;
&lt;p&gt;There are people who embrace the easy model of producing free content. And by flooding the internet with potentially inaccurate content, we will not only bias the future training process of AI—we will damage the future knowledge of human beings.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The real danger is not that computers will begin to think like men, but that men will begin to think like computers.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— Sydney J. Harris&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I tend to think of myself as a realist. Although, if you ask some of the people who know me well—they might say I’m leaning towards pessimism. AI is here. We’ve opened that box. As with every tool ever created by human beings—it can be used for good, and it can be used for bad. People, usually, tend to focus on the good. I’m not going to unwrap my entire philosophical views here, but I tend to, typically, focus on the bad.&lt;/p&gt;
&lt;p&gt;We reward content creation. Aside from Google, the 4 most visited websites are YouTube, Facebook, Twitter, and Instagram (&lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_most_visited_websites&quot;&gt;source&lt;/a&gt;); and the 3 most downloaded apps are TikTok, Instagram, and Facebook. Those are all content creation tools. I’ve read somewhere that today’s kids, when you ask them who they want to be when they grow up, answer &lt;em&gt;a YouTuber&lt;/em&gt; or a &lt;em&gt;TikTok/Instagram influencer&lt;/em&gt;. And we value knowledge.&lt;/p&gt;
&lt;p&gt;ChatGPT, DALL-E, Stable Diffusion, Midjourney, Copilot etc. Those are all great tools for both content creation and knowledge sharing. Every one of us will have to find a place for them in our daily toolbox. ChatGPT crossed 1 million users in less than a week—this is a clear sign that it’s not just a tool that will be forgotten soon.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The striking thing about the reaction to ChatGPT is not just the number of people who are blown away by it, but who they are. These are not people who get excited by every shiny new thing. Clearly something big is happening.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— &lt;strong&gt;Paul Graham&lt;/strong&gt;, Computer Scientist, Entrepreneur, Venture Capitalist, and Author, &lt;cite&gt;&lt;a href=&quot;https://twitter.com/paulg/status/1598698665337561088&quot;&gt;Twitter&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;AI here to stay—and more will come. But together with the adoption of those tools into our daily toolboxes—we need to have a discussion about topics such as creativity, attribution, respect for human work and the effects those tools might have on the future of humanity and our knowledge.&lt;/p&gt;
&lt;p&gt;#WrittenByHuman&lt;/p&gt;</content:encoded></item><item><title>Cursor based pagination</title><link>https://kudmitry.com/articles/cursor-based-pagination/</link><guid isPermaLink="true">https://kudmitry.com/articles/cursor-based-pagination/</guid><pubDate>Wed, 07 Dec 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Traditional pagination existed for many years. But it’s not useful for platforms with huge amount of user generated content. Luckily, there is an alternative solution for such systems.&lt;/p&gt;
&lt;h2 id=&quot;traditional-pagination&quot;&gt;Traditional pagination&lt;/h2&gt;
&lt;p&gt;Before we jump into the solution, let’s look at traditional pagination and it’s drawbacks. Let’s say we have a database table for forum posts that looks like this:&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;th&gt;Content&lt;/th&gt;&lt;th&gt;LastActiveDate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Some post&lt;/td&gt;&lt;td&gt;Some content&lt;/td&gt;&lt;td&gt;2022-11-26T14:00:00&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;In this case, &lt;code&gt;LastActiveDate&lt;/code&gt;—indicates the date when the post was last active (i.e., a comment was published).&lt;/p&gt;
&lt;p&gt;We want to display a front page with all the posts sorted by &lt;code&gt;LastActiveDate&lt;/code&gt;. Since we don’t know how much data we can have, we decide on an arbitrary &lt;strong&gt;page size&lt;/strong&gt; of 10. And then, in order to display the posts on a particular page, we will use the following query:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; * &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; posts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;ORDER BY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; LastActiveDate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;LIMIT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ${pageSize}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;OFFSET ${(currentPage - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) * pageSize}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The math is very simple. Given a specific page, say &lt;strong&gt;2&lt;/strong&gt;, we will select all items starting from offset &lt;code&gt;(2 - 1) * 10 = 10&lt;/code&gt;, limiting the selection to 10 elements, and the result will be all the items in group of &lt;code&gt;[10,20)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Usually, with traditional pagination, we also need to have another query in order to generate the visual pagination itself. And for that we need to know the total amount of pages we have, which can be done by:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; totalItems&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; sql&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;SELECT count(ID) FROM posts&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; totalPages&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Math&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;ceil&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;totalItems&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; pageSize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;drawbacks&quot;&gt;Drawbacks&lt;/h3&gt;
&lt;p&gt;This pagination style, however, has one major drawback. Let’s say I’ve navigated to page 1 of the forum and then decided to go make some tea. After coming back and seeing that no posts on the first page are of interest to me, I navigate to the second page and find out that it includes 2 posts from the first page.&lt;/p&gt;
&lt;p&gt;This happens because during the time I was making and drinking my tea, some older posts that were not among the 10 posts on the first page — were updated, hence changing their &lt;code&gt;LastActiveDate&lt;/code&gt; to be the most recent. Upon navigating to the second page, I now see 10 more posts, but because of the shift in order, 2 posts from the first 10 — were pushed into the second 10 on the second page.&lt;/p&gt;
&lt;p&gt;Usually, on platforms with low update rates, like a small forum — this drawback is not that critical. Occasionally users will see content on the next page, which appeared on the previous page — but the frequency during which it happens — is low. We can increase the page size to say 15, 20, 25, or even 30 posts per page — and since forums often have activity concentrated in few popular or new posts on the first page, the chance of seeing the same data on next pages — is small, and we can live with that.&lt;/p&gt;
&lt;p&gt;However, for high throughput platforms like Twitter, or machine-to-machine streaming services — this case is unacceptable. Image navigating Twitter only to see the same content over and over again, because the rate at which content is created on Twitter is rapid, and thus content from the previous page is always pushed down to the next page.&lt;/p&gt;
&lt;p&gt;In order to solve this problem, let me introduce — Cursor based pagination.&lt;/p&gt;
&lt;h2 id=&quot;cursor-based-pagination&quot;&gt;Cursor based pagination&lt;/h2&gt;
&lt;p&gt;Let’s say we want our forum pagination to never display data from previous pages. When a user navigates to the second page — she will always see data that suppose to be on that page. This can be achieved with cursor based pagination.&lt;/p&gt;
&lt;h3 id=&quot;how-cursor-based-pagination-works&quot;&gt;How cursor based pagination works&lt;/h3&gt;
&lt;p&gt;In cursor based pagination, we do not have logical pages. Instead, each page returns the data that suppose to be displayed on that page as well as a &lt;strong&gt;token&lt;/strong&gt; to retrieve the next page. The token is usually opaque, and for the end user it has no meaning — but our system knows to read it and generate the next page based on it.&lt;/p&gt;
&lt;p&gt;This brings me to the first drawback of cursor based pagination:&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;warning&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;WARNING&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Cursor based pagination is &lt;strong&gt;forward&lt;/strong&gt;. It does not support going back (i.e. navigating from second page back to first, or from third back to second); nor it supports &lt;em&gt;skipping&lt;/em&gt; pages (i.e. going to page 5 from page 2).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On a platform like forum, this might not be very useful. Forums are typically meant to be &lt;em&gt;browsed&lt;/em&gt;, so users always can jump to a specific page, navigate backwards and forwards, or event jump to the last page. However, — on platforms with &lt;em&gt;infinite&lt;/em&gt; scrolling navigation, like feeds, this solution is perfect.&lt;/p&gt;
&lt;h3 id=&quot;cursor-based-pagination-example&quot;&gt;Cursor based pagination example&lt;/h3&gt;
&lt;p&gt;Let’s say our product manager came up with the terrible idea of turning our 90s looking forum with traditional pagination, to 2000s looking forum with infinite scroll feed. We’ve tried to warn him that this is a bad idea, but he insisted that users wanted it. And so let’s turn our forums’ traditional pagination — to cursor based instead.&lt;/p&gt;
&lt;p&gt;First, we need to select the cursor. Since forum posts are often sorted in chronological order from newest to oldest post, it makes sense to use the &lt;code&gt;LastActiveDate&lt;/code&gt; as a cursor. Hence, the retrieval of a particular pages’ data can be done like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getPosts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;cursor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; posts&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; table&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;posts&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;orderBy&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;LastActiveDate&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;DESC&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;limit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PAGE_SIZE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;where&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;q&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cursor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        q&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;where&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;LastActiveDate&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;&amp;#x3C;&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cursor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;posts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;nextPageCursor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;posts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; posts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;posts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;LastActiveDate&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: The query builder is a pseudocode, but can be achieved with tools like &lt;a href=&quot;https://knexjs.org/&quot;&gt;KnexJS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For every call, we fetch the first &lt;code&gt;PAGE_SIZE&lt;/code&gt; posts and provide a cursor to the next page, which is equal to the &lt;code&gt;LastActiveDate&lt;/code&gt; of the last post in our current batch (or &lt;code&gt;null&lt;/code&gt; if no posts exist). If the &lt;code&gt;cursor&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; — we start from the first page.&lt;/p&gt;
&lt;p&gt;Since our posts are always sorted in chronologically descending order, our pagination becomes consistent.&lt;/p&gt;
&lt;p&gt;Let’s try to do a visualization. Let’s imagine we have the following database:&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;th&gt;….&lt;/th&gt;&lt;th&gt;LastActiveDate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Title 1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-10T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Title 2&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-09T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Title 3&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-08T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Title 4&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-07T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Title 5&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-06T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I’ve intentionally sorted the posts by &lt;code&gt;LastActiveDate&lt;/code&gt; to easier visualize the process. Obviously, the data does not need to be sorted in advance, since SQLs &lt;code&gt;SORT BY LastActiveDate DESC&lt;/code&gt; handles the sorting.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For the sake of simplicity, let’s set our page size to 2. Upon first execution, we will get posts 1 and 2 and our &lt;code&gt;nextPageCursor&lt;/code&gt; will be equal to &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt; (which is the &lt;code&gt;LastActiveDate&lt;/code&gt; of the second post).&lt;/p&gt;
&lt;p&gt;As we would like to retrieve the next page, we will provide the &lt;code&gt;nextPageCursor&lt;/code&gt; to our function, and since the query looks at posts older than &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt; we will get two more posts — IDs 3 and 4.&lt;/p&gt;
&lt;p&gt;Now — let’s come back to our tea scenario. Imagine I’m looking at the forum’s first page with posts 1 and 2, and before clicking &lt;em&gt;next&lt;/em&gt;, I go to drink tea. During that time, someone posts a new comment in post ID 3 — thus altering its &lt;code&gt;LastActiveDate&lt;/code&gt; to be today (let’s say today is &lt;code&gt;2022-01-11T00:00:00&lt;/code&gt;). The table will now look like this:&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;th&gt;….&lt;/th&gt;&lt;th&gt;LastActiveDate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Title 3&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-11T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Title 1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-10T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Title 2&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-09T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Title 4&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-07T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Title 5&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2022-01-06T00:00:00&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;When I come back to the computer, I click &lt;em&gt;next page&lt;/em&gt;. In our traditional navigation — my action would have returned posts with IDs 2 and 4, thus “leaking” the second post from the first page to the second.&lt;/p&gt;
&lt;p&gt;Cursor based navigation, with the cursor of &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt;, would however return posts with IDs 4 and 5 as expected. Notice how post ID 3 is skipped and will never be seen by me, unless I start over from page 1. It’s important to point out that this flaw is not a problem of the cursor based pagination — but rather a problem with the way we’ve &lt;em&gt;implemented&lt;/em&gt; cursor based pagination. Different use cases — have different implementations. It is possible to implement cursor base pagination which takes into account newer content as well. It, however, makes little sense to do so in a context of a forum, since in forums — going forward in pagination — means seeing older content.&lt;/p&gt;
&lt;p&gt;As I’ve said before, we do lose some functionality — namely: going backwards (from page 2 to page 1 for example) and skipping pages (from page 2 to page 7 for example). So this approach is not useable for &lt;em&gt;browsing&lt;/em&gt; content in a paginated manner, but it is a good approach to implement infinite scrolling without encountering duplicate content.&lt;/p&gt;
&lt;h3 id=&quot;bonusdont-use-dates-and-or-timestamps-as-cursor&quot;&gt;Bonus—don’t use dates and or timestamps as cursor&lt;/h3&gt;
&lt;p&gt;I could have ended the article here, but I’m pretty sure if I did so — I’d get criticized for using date as a cursor. And this will be a well deserved criticism.&lt;/p&gt;
&lt;p&gt;You see — the entire point of cursor based navigation — is to create a consistent stream of data without repetitions. Yes — you give away some features, but in exchange you get fresh data for each page. As I’ve said — some systems might benefit from it.&lt;/p&gt;
&lt;p&gt;However, — by using dates as cursors — you run into the same problem. You can encounter content repetition. Let’s assume we have a high traffic forum with a very active community. Let’s look at a subset of such forums posts table:&lt;/p&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;th&gt;….&lt;/th&gt;&lt;th&gt;LastActiveDate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;675&lt;/td&gt;&lt;td&gt;Title 1&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:31:22&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;123&lt;/td&gt;&lt;td&gt;Title 2&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:30:48&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;534&lt;/td&gt;&lt;td&gt;Title 3&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:30:23&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;301&lt;/td&gt;&lt;td&gt;Title 4&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:30:23&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;231&lt;/td&gt;&lt;td&gt;Title 5&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:30:23&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;945&lt;/td&gt;&lt;td&gt;Title 6&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;2022-01-10T12:30:17&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: as earlier, the data is sorted by &lt;code&gt;LastActiveDate&lt;/code&gt; for demonstration purposes only.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For the sake of example, let’s say our page size is 2. And since we are looking at a subset of data, let’s say we hold a &lt;code&gt;nextPageCursor&lt;/code&gt; of &lt;code&gt;2022-01-10T12:31:22&lt;/code&gt;. What will be the result of the invocation of &lt;code&gt;getPosts(&quot;2022-01-10T12:31:22&quot;);&lt;/code&gt;? If you’ve read through the article and understood it well, you would be correct to answer: posts 123 and 534. And what will be the &lt;code&gt;nextPageCursor&lt;/code&gt; value for that invocation? The correct answer is &lt;code&gt;2022-01-10T12:30:23&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, here’s the problem. Let’s say you want to navigate to the next page using &lt;code&gt;nextPageCursor = &apos;2022-01-10T12:30:23&apos;&lt;/code&gt;. What would be the result? That’s true — post ID 945. Since the query we’ve written earlier, translates to:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; * &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; posts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;ORDER BY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; LastActiveDate &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;DESC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;WHERE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; LastActiveDate &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;2022-01-10T12:30:23&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;LIMIT&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are skipping posts ID 301 and ID 231. The hasty among you might just run and replace &lt;code&gt;&amp;#x3C;&lt;/code&gt; to &lt;code&gt;&amp;#x3C;=&lt;/code&gt;, but this introduces another issue. Now, instead of getting post ID 945 you get posts ID 534 and ID 301. So not only you are getting post ID 534 &lt;strong&gt;again&lt;/strong&gt;, your &lt;code&gt;nextPageCursor&lt;/code&gt; is set to &lt;code&gt;2022-01-10T12:30:23&lt;/code&gt; so you’re essentially stuck in an endless loop (unless someone decides to update one of the posts).&lt;/p&gt;
&lt;p&gt;Dates, although provide chronological order, &lt;strong&gt;are not&lt;/strong&gt; providers of uniqueness. In a system with high rate of content, you might end up with collisions. And I know what you are thinking about: &lt;em&gt;I’ll just add milliseconds!&lt;/em&gt; While milliseconds &lt;em&gt;might&lt;/em&gt; help you to prevent this issue in human generated content (although there is still a chance of collision, depending on the traffic and the nature of the system) — they are not a solution for machine generated content in systems with high rate of content generation. There are other solutions, like content versioning, to provide uniqueness of the cursor.&lt;/p&gt;
&lt;p&gt;Also keep in mind that cursors do not have to be bound to one specific dimension. I once designed a system where the cursor was a JSON that held multiple vectors for data retrieval. This JSON was then &lt;code&gt;base64&lt;/code&gt; encoded and was used as a cursor for navigation. So you could combine multiple fields, and any other data you would want, inside the cursor. As long as you can pass the cursor in the URL and your system can decode it — it’s a viable solution.&lt;/p&gt;</content:encoded></item><item><title>ChatGPT, AI, and the future of tech</title><link>https://kudmitry.com/articles/chatgpt-ai-and-the-future-of-tech/</link><guid isPermaLink="true">https://kudmitry.com/articles/chatgpt-ai-and-the-future-of-tech/</guid><pubDate>Mon, 05 Dec 2022 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Unless you’ve been living under a rock, you probably heard about OpenAI releasing ChatGPT for a free research preview.&lt;/p&gt;
&lt;p&gt;After the release, a bunch of different posts across Reddit and Hacker News started to emerge — outlining all the possible reasons why AI and ChatGPT will now replace jobs such as: Product managers, software engineers, tech bloggers etc. Usage of ChatGPT exploded, leading to sites like StackOverflow — to implement temporary policy to ban ChatGPT generated answers (&lt;a href=&quot;https://meta.stackoverflow.com/questions/421831/temporary-policy-chatgpt-is-banned&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Look — I’m a technology lover. I like messing with technology; I like using technology to improve my life. I automate certain tasks using software; I adore my robotic vacuum and window cleaners, and after getting a dishwasher last year — I don’t understand why I didn’t get one earlier. But, I, personally, can’t share the enthusiasms or doom–like prophecies that we all, are soon, going to be replaced by AI.&lt;/p&gt;
&lt;h2 id=&quot;understanding-ai&quot;&gt;Understanding AI&lt;/h2&gt;
&lt;p&gt;I’m not an AI engineer, and apart from messing a little with artificial neural networks and genetic algorithms many years ago, my AI knowledge is very limited. According to my understanding, AI, and especially the GPT–3 model — built upon feeding on initial content. This means that those models are analyzing existing texts or source code — and learn to generate meaningful content based on that. According to Wikipedia, GPT–3 was trained mostly on crawled websites (&lt;a href=&quot;https://en.wikipedia.org/wiki/GPT-3#Training_and_capabilities&quot;&gt;source&lt;/a&gt;). The famous GitHub CoPilot was trained on source code from GitHub repositories. And so, in my uneducated opinion, current AI models are limited by the knowledge we — humans — have created.&lt;/p&gt;
&lt;h2 id=&quot;taking-ai-for-a-ride&quot;&gt;Taking AI for a ride&lt;/h2&gt;
&lt;p&gt;At first I was skeptical, but ChatGPT was topping Reddit and Hacker News front pages, as well as talked a lot among my colleagues, so I’ve decided to take it for a ride. It’s open for public, for a free research preview: &lt;a href=&quot;https://chat.openai.com/&quot;&gt;https://chat.openai.com/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;hey-ai--write-me-a-blog-post&quot;&gt;Hey AI – write me a blog post&lt;/h3&gt;
&lt;p&gt;Before jumping to find whether I’ll be replaced as software engineer, I was wondering will I be replaced as a blogger. On Wednesday, I’m going to publish a new blog post that talks about cursor based pagination. You should totally subscribe to my &lt;a href=&quot;https://yieldnews.substack.com&quot;&gt;newsletter&lt;/a&gt; to be notified when it’s published, as well as stay updated about any future posts. But in the meantime, I wanted to see — would ChatGPT be able to generate a blog post on that topic. And so I’ve asked AI the following:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;write a blog post about cursor based pagination and comparison with traditional pagination, including examples for usage as well as cons and pros of each approach&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It generated a nice post, but when it came to comparing pros and cons — the results became messy. It listed 3 pros for cursor based pagination, and while the first two were okay-ish, the third one is a big miss:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;It’s more user-friendly. With traditional pagination, it’s difficult for the user to know how many pages there are in total, or to go directly to a specific page. With cursor based pagination, the user can see the cursor value and use it to navigate to a specific position in the dataset.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is a complete lie. There is an easy way to both (1) know how many pages are there in total, and (2) go directly to a specific page. We’ve been doing it for decades. Moreover, depending on the implementation, the user is not always able to know and use the cursor as he desires. On complex datasets — cursors are opaque and meaningless to the user.&lt;/p&gt;
&lt;p&gt;You could say that this is a mistake that can be easily fixed by a human later on, but wait! There’s more! When ChatGPT generated the cons of cursor based pagination, it wrote this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;It’s less predictable. In traditional pagination, the user always knows how many records will be on each page. With cursor based pagination, the number of records on each page can vary depending on how many records have been added or deleted.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The first sentence contradicts the pro outlined earlier. If I know how many records are on each page, I can know how many pages there are. The second sentence — is not entirely true, in fact it is possible (and recommended) to include a limit on the amount of records you retrieve using any type of pagination.&lt;/p&gt;
&lt;p&gt;While I am amazed by the ability to generate, &lt;em&gt;generally&lt;/em&gt;, good structured and informative post — it’s far from replacing a human, such as myself, who performed a good research combined with personal, and professional experience.&lt;/p&gt;
&lt;h3 id=&quot;sudo-ai--generate-me-some-code&quot;&gt;sudo ai – generate me some code&lt;/h3&gt;
&lt;p&gt;I’m safe with writing blog posts. Let’s see if I can be replaced as a software engineer.&lt;/p&gt;
&lt;p&gt;I’ve asked ChatGPT to generate me some code:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Write a function that streams data from external API endpoint and caches it, using typescript and nodejs&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And it produced the following result:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; request&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;request&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; fs&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;fs&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Function for streaming data from external API endpoint and caching it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; streamData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;cacheFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;    // Check if the cache file exists.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;access&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cacheFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;constants&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;F_OK&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Cache file does not exist, so create it and steam data from the API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; fileStream&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createWriteStream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cacheFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;        request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileStream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;finish&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Cache file exists, so read data from the cache file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        fs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;readFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cacheFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;          if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ChatGPT suggested using a deprecated module — &lt;code&gt;request&lt;/code&gt;. Also, the code itself will fail linting because &lt;code&gt;streamData&lt;/code&gt; is defined as &lt;code&gt;async&lt;/code&gt; but there is no need for that function to be async, since the code uses callback style &lt;code&gt;fs&lt;/code&gt; and has no &lt;code&gt;await&lt;/code&gt; statements. Nitpicking, I know.&lt;/p&gt;
&lt;p&gt;When I asked ChatGPT, what is wrong with the above code — it was smart enough to output this:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;One potential issue with this code is that it uses the &lt;code&gt;request&lt;/code&gt; module, which has been deprecated and is no longer recommended for use. Instead of using the &lt;code&gt;request&lt;/code&gt; module, the code should use a more modern HTTP client, such as the &lt;code&gt;axios&lt;/code&gt; library, to make HTTP requests.&lt;/p&gt;&lt;p&gt;Another potential issue with this code is that it does not handle errors properly. In the case where the &lt;code&gt;request&lt;/code&gt; module throws an error, the code does not reject the promise, which means that the error will be silently ignored. To fix this, the code should add a &lt;code&gt;catch&lt;/code&gt; block to the &lt;code&gt;request&lt;/code&gt; call, and reject the promise with the error if one occurs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It was able to catch the deprecated &lt;code&gt;request&lt;/code&gt; module, but said nothing on the unnecessary &lt;code&gt;async&lt;/code&gt; keyword, or suggested to use &lt;code&gt;fsPromises&lt;/code&gt; instead of callbacks for a more readable code.&lt;/p&gt;
&lt;h2 id=&quot;programming-is-more-than-just-writing-code&quot;&gt;Programming is more than just writing code&lt;/h2&gt;
&lt;p&gt;For some reason, people keep thinking that a programmer’s job is to write code, while in reality — our job is translating client requirements into code as well as designing, and maintaining complex systems. Writing new, fresh code — is relatively small part compared to maintenance and modification of existing code.&lt;/p&gt;
&lt;p&gt;Questions, such as &lt;em&gt;how to sort an array&lt;/em&gt;, were long ago solved with all possible algorithms. ChatGPT is no different from Google search for such question, except for being more convenient than performing your own search, while potentially producing wrong or outdated code. The power of communities such as StackOverflow — is that answers are audited and updated. I keep getting notification about new comments or updates to a &lt;a href=&quot;https://stackoverflow.com/questions/28459418/use-of-put-vs-patch-methods-in-rest-api-real-life-scenarios&quot;&gt;question&lt;/a&gt; I’ve asked more than 7 years ago!&lt;/p&gt;
&lt;p&gt;Don’t get me wrong — those are amazing results. I’m happy for the OpenAI team, and the people behind GPT–3 are, undoubtedly, incredible and &lt;a href=&quot;https://www.toptal.com/artificial-intelligence#hiring-guide&quot;&gt;talented engineers&lt;/a&gt;. But I believe that current AI models are limited by our existing knowledge. They are unable to &lt;strong&gt;create&lt;/strong&gt; new things. They are able to &lt;strong&gt;remix&lt;/strong&gt; existing knowledge. Takes bits from different sources, and &lt;em&gt;try&lt;/em&gt; to put them in context. Those tools, in their current state, can be a great addition to other tools like searching in Google or using StackOverflow. But I don’t believe we are anywhere close to having an AI that can truly create new things — and thus replacing us in our jobs. Don’t trust me? At least trust an AI, then.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/ai_replace_me_r0rokp.webp&quot; alt=&quot;ChatGPT on replacing software engineers#890x417&quot;&gt;&lt;figcaption&gt;ChatGPT on replacing software engineers#890x417&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;#WrittenByHuman&lt;/p&gt;</content:encoded></item><item><title>Why you should adopt Makefile in all of your projects</title><link>https://kudmitry.com/articles/why-you-should-adpot-makefile-in-all-of-your-projects/</link><guid isPermaLink="true">https://kudmitry.com/articles/why-you-should-adpot-makefile-in-all-of-your-projects/</guid><pubDate>Wed, 23 Nov 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GNU Make. A software that is, most likely, older than you. It’s so simple, so standard, and so ignored. I’m here to provide a case in favor of &lt;code&gt;make&lt;/code&gt; and Makefiles.&lt;/p&gt;
&lt;h2 id=&quot;what-is-make-and-makefile&quot;&gt;What is &lt;code&gt;make&lt;/code&gt; and Makefile?&lt;/h2&gt;
&lt;p&gt;Before I even start to build my case, I need to explain to you what is &lt;code&gt;make&lt;/code&gt; and Makefile.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make&lt;/code&gt; — is a build automation tool. It is written in C and was first released in April 1976. On Linux, &lt;code&gt;make&lt;/code&gt; is usually available as part of the build tools dependencies that includes stuff like &lt;code&gt;gcc&lt;/code&gt; — the C compiler etc. On Ubuntu, it’s under &lt;code&gt;build-essentials&lt;/code&gt;; on Arch Linux, under &lt;code&gt;base-devel&lt;/code&gt;. MacOS users can install it with &lt;code&gt;brew&lt;/code&gt;. And on Windows… I honestly have to clue what is going-on on Windows.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;make&lt;/code&gt; does not work by itself. It needs instructions. And those instructions are stored in a Makefile.&lt;/p&gt;
&lt;h3 id=&quot;makefile-101&quot;&gt;Makefile 101&lt;/h3&gt;
&lt;p&gt;Makefile is a file named… &lt;code&gt;Makefile&lt;/code&gt;. Makefile consists of rules. Each rule has a target, optional dependencies (which can be other targets or files — more on that later) and a set of commands.&lt;/p&gt;
&lt;p&gt;A simple Makefile that complies &lt;code&gt;index.ts&lt;/code&gt; into &lt;code&gt;index.js&lt;/code&gt; might look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;build:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	npx tsc index.ts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to compile the project, you execute &lt;code&gt;make&lt;/code&gt; with a target name:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;build&lt;/code&gt; — is the name of the target. Then each next line that starts with a tab is a command that make will execute. The above example will compile &lt;code&gt;index.ts&lt;/code&gt; and place the resulting &lt;code&gt;index.js&lt;/code&gt; in the same folder. I can modify the commands and move the resulting file into &lt;code&gt;dist&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;build:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	mkdir -p dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	npx tsc index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	mv index.js dist&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Of course, in case of TypeScript — you should avoid this practice and instead set up a proper &lt;code&gt;tsconfig.json&lt;/code&gt;, since TypeScript compiler handles that for you. But for the sake of demonstration, I’ll continue with this example as it’s easier to understand.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I’ve mentioned earlier that make targets can have dependencies which might be other make targets or files. Let’s explore this concept. Say we have the following directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/workspace/make_test ❯ tree -I node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Makefile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; └──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; yarn.lock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; directory,&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A typical TypeScript project. We want to compile it with &lt;code&gt;make&lt;/code&gt; and put the resulting files in &lt;code&gt;dist&lt;/code&gt; folder (which might or might not exist and might or might not have previously compiled files). Here’s a Makefile that allows this.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prepare:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	mkdir -p dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;clean: prepare&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	rm -rf dist/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dist/index.js: src/index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	npx tsc $&amp;#x3C; --outfile $@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;build: clean dist/index.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I run &lt;code&gt;make build&lt;/code&gt;, I get the following output:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/workspace/make_test ❯ make build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -rf&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; tsc&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; src/index.ts&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --outfile&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist/index.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And running &lt;code&gt;tree&lt;/code&gt; reveals that I now have a compiled file in &lt;code&gt;dist&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Makefile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; └──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; index.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;│  &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; └──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; yarn.lock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; directories,&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 5&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s understand step by step what’s going on.&lt;/p&gt;
&lt;p&gt;First, we have a target named &lt;code&gt;prepare&lt;/code&gt;. This target has only one command that creates a directory named &lt;code&gt;dist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, we have a target named &lt;code&gt;clean&lt;/code&gt;. The purpose of this target is to delete the contents of &lt;code&gt;dist&lt;/code&gt;. But if you’ve noticed, unlike &lt;code&gt;prepare&lt;/code&gt; which does not have anything after the double colon, &lt;code&gt;clean&lt;/code&gt; specifies &lt;code&gt;prepare&lt;/code&gt;. This is called a dependency. In order to run &lt;code&gt;clean&lt;/code&gt;, it will execute &lt;code&gt;prepare&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;Then we have another target called &lt;code&gt;dist/index.js&lt;/code&gt;. This time it’s a file, and it depends on… you’ve guessed it — another file called &lt;code&gt;src/index.ts&lt;/code&gt;. Targets in &lt;code&gt;make&lt;/code&gt;, as I’ve said earlier, can depend on other targets or on files. In order to satisfy the file dependency, &lt;code&gt;make&lt;/code&gt; makes sure that the file exist. Make does not really differentiate between targets and files. If you create a file named &lt;code&gt;prepare&lt;/code&gt; and run &lt;code&gt;make build&lt;/code&gt; again, you can see that this time the output is different:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/workspace/make_test ❯ make build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -rf&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; tsc&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; src/index.ts&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --outfile&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist/index.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make is no longer executing &lt;code&gt;mkdir -p dist&lt;/code&gt;, because make sees that a file named &lt;code&gt;prepare&lt;/code&gt; already exists, therefor this target is already satisfied. The same is true for &lt;code&gt;src/index.ts&lt;/code&gt;. In practice — it’s a target. But our Makefile does not have a definition for that target. If you go on and delete (or rename) &lt;code&gt;src/index.ts&lt;/code&gt;, and run &lt;code&gt;make build&lt;/code&gt; again — you will get an error:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/workspace/make_test ❯ make build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -rf&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; dist/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;make:&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ***&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; No&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; rule&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; make&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; target&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; `&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;src/index.ts&apos;, needed by `dist/index.js&apos;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  Stop.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because make can’t find the file &lt;code&gt;src/index.ts&lt;/code&gt;, nor it can find a target named &lt;code&gt;src/index.ts&lt;/code&gt; in our Makefile — it fails. Remember — &lt;code&gt;make xyz&lt;/code&gt; first checks for the existence of a file named &lt;code&gt;xyz&lt;/code&gt;, and if no such file is found — it looks for a target named &lt;code&gt;xyz&lt;/code&gt;, and if none found — &lt;code&gt;make&lt;/code&gt; fails. Also if you’ve noticed — make does not do rollbacks. It simply executes commands until it’s done or until failure.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side note: there is a way to instruct &lt;code&gt;make&lt;/code&gt; that a target is not a file by using what is known as phony targets, but for the sake of simplicity, I’m not going to cover that here. You can read more about &lt;a href=&quot;https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html&quot;&gt;phony targets&lt;/a&gt; if you want.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Going back to our &lt;code&gt;dist/index.js&lt;/code&gt;. This target executes one command &lt;code&gt;npx tsc $&amp;#x3C; --outfile $@&lt;/code&gt;. There are two variables there: &lt;code&gt;$&amp;#x3C;&lt;/code&gt; — is the name of the first dependency, in our case &lt;code&gt;src/index.ts&lt;/code&gt; while &lt;code&gt;$@&lt;/code&gt; is the name of the target (&lt;code&gt;dist/index.ts&lt;/code&gt;). And so running this command translates to &lt;code&gt;npx tsc src/index.ts --outfile dist/index.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;_Make has a robust mechanism for variables and looping over files. You can, for example, pick all the &lt;code&gt;_.ts&lt;/code&gt;files and compile each and one of them using the&lt;code&gt;$&amp;#x3C;&lt;/code&gt; and &lt;code&gt;$@&lt;/code&gt;parameters. I’m not going to cover it here since it’s not a tutorial about&lt;code&gt;make&lt;/code&gt;. Drop me an email if you want me to write one (my contact details are at the bottom of this post).*&lt;/p&gt;
&lt;p&gt;Finally, we have the &lt;code&gt;build&lt;/code&gt; target. This target depends on two other targets — the &lt;code&gt;clean&lt;/code&gt; target and the &lt;code&gt;dist/index.js&lt;/code&gt; target, and by itself it executes no commands.&lt;/p&gt;
&lt;p&gt;Makefile has more cool features such as support for variables and macros, including sub-makefiles etc. For the sake of simplicity, I’m not going to cover them here. You can take a look at my decade old &lt;a href=&quot;https://github.com/skwee357/wrap-os/blob/master/Makefile&quot;&gt;Makefile&lt;/a&gt; for a WIP OS I was once writing. It includes variables, macros, sub-makefiles etc.&lt;/p&gt;
&lt;h2 id=&quot;but-but-this-looks-like-your-favorite-build-tool-name&quot;&gt;But… but… this looks like &lt;em&gt;[your favorite build tool name]&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Exactly! If you are familiar with &lt;a href=&quot;/tag/javascript&quot;&gt;JavaScript&lt;/a&gt; then you are probably familiar with &lt;code&gt;npm&lt;/code&gt; scripts. In &lt;a href=&quot;/tag/rust&quot;&gt;Rust&lt;/a&gt; we have &lt;code&gt;cargo&lt;/code&gt;. In Go, we have the &lt;code&gt;go&lt;/code&gt; build tool. When using Python, we have &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;pipenv&lt;/code&gt;, &lt;code&gt;potery&lt;/code&gt; and &lt;code&gt;conda&lt;/code&gt; for dependency management. For Java there are &lt;code&gt;maven&lt;/code&gt;, &lt;code&gt;gradle&lt;/code&gt; and &lt;code&gt;ant&lt;/code&gt;. In Ruby, dependencies are managed with &lt;code&gt;gem&lt;/code&gt;. And honestly — it’s too much.&lt;/p&gt;
&lt;p&gt;Here’s the thing. Developers are lazy. When you jump from project to project — managing dependencies and compiling the code becomes a headache. Even if you don’t change the language, only in JavaScript we had &lt;code&gt;grunt&lt;/code&gt;, &lt;code&gt;gulp&lt;/code&gt; and &lt;code&gt;bower&lt;/code&gt; which are dead long ago, but might still be in use for some legacy project you are working with; but even today we are torn between &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt;, and I didn’t mention frontend where there are multiple bundlers to choose from. I tend to jump between projects written in &lt;a href=&quot;/tag/javascript&quot;&gt;JavaScript&lt;/a&gt;, TypeScript, Java, Python, Ruby, and &lt;a href=&quot;/tag/rust&quot;&gt;Rust&lt;/a&gt;. So I need to remember a handful of script runners and dependency management tools.&lt;/p&gt;
&lt;p&gt;And I don’t want to do 2 things: (a) Write a comprehensive &lt;code&gt;README.md&lt;/code&gt; file that describes how to install, build and run my project, and (b) Read a comprehensive &lt;code&gt;README.md&lt;/code&gt; file that describes how to install, build and run your project. I want to be productive by simply jumping into the codebase and producing code, rather than spending my time on trying to figure out what dependency management system the project uses and how to run tests.&lt;/p&gt;
&lt;p&gt;And here’s where make and Makefile comes in. &lt;code&gt;make&lt;/code&gt; exists for 46 years. It’s not going away. By providing Makefile and common make targets like &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;compile&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; — I can easily jump into a project and start being productive. I don’t need to scout multiple files, trying to come up with the winning combination. &lt;em&gt;Aha! You have a &lt;code&gt;package.json&lt;/code&gt;, but a &lt;code&gt;yarn.lock&lt;/code&gt; so this means I need to use yarn install&lt;/em&gt;. It’s a mental overhead I don’t want to deal with.&lt;/p&gt;
&lt;p&gt;Moreover, designing a proper CI pipeline becomes a breeze. My CI will execute &lt;code&gt;make&lt;/code&gt; targets, and under the hood I can use whatever dependency management / script runner tool that I like. Hell, I can even change them every 5 days when a new hyped, &lt;em&gt;blazingly fast&lt;/em&gt;, build / script runner tool appears.&lt;/p&gt;
&lt;p&gt;Use whatever you like. You like &lt;code&gt;yarn&lt;/code&gt;? Go ahead! Use &lt;code&gt;yarn&lt;/code&gt;. You can even put your commands in &lt;code&gt;package.json&lt;/code&gt; if you want to (for example to utilize &lt;code&gt;pre*&lt;/code&gt; or &lt;code&gt;post*&lt;/code&gt; hooks), but please provide a &lt;code&gt;Makefile&lt;/code&gt; that acts as a facade to your &lt;code&gt;npm&lt;/code&gt; scripts. That way, all we need to look at is only one single &lt;code&gt;Makefile&lt;/code&gt; to understand how to run, build, compile and test the project.&lt;/p&gt;</content:encoded></item><item><title>4 Ways to Minimize your Dependencies in Node.js</title><link>https://kudmitry.com/articles/minimize-your-dependencies-in-nodejs/</link><guid isPermaLink="true">https://kudmitry.com/articles/minimize-your-dependencies-in-nodejs/</guid><pubDate>Thu, 03 Nov 2022 17:30:00 GMT</pubDate><content:encoded>&lt;p&gt;We all know the joke about how &lt;code&gt;node_modules&lt;/code&gt; is the heaviest object in the universe.&lt;/p&gt;
&lt;p&gt;For example, a project that uses only &lt;code&gt;fastify&lt;/code&gt;, &lt;code&gt;knex&lt;/code&gt;, &lt;code&gt;typescript&lt;/code&gt;, and &lt;code&gt;uuid&lt;/code&gt; generates an 83MB &lt;code&gt;node_modules&lt;/code&gt; folder! That’s huge! And those four packages are far from a complete set for a relatively small back-end. A more realistic size for &lt;code&gt;node_modules&lt;/code&gt; is north of 100MB, in some cases reaching 1GB.&lt;/p&gt;
&lt;p&gt;In this post, we’ll explore four methods to minimize your code’s dependencies, resulting in faster CI/CD execution and safer code.&lt;/p&gt;
&lt;p&gt;But first, let’s touch on some of the problems with heavy &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-issues-with-heavy-node_modules-in-nodejs&quot;&gt;The Issues with Heavy &lt;code&gt;node_modules&lt;/code&gt; in Node.js&lt;/h2&gt;
&lt;p&gt;Heavy &lt;code&gt;node_modules&lt;/code&gt; can cause slower CI/CD pipelines, as dependencies are usually installed during those pipelines, and require network calls to a registry (whether &lt;code&gt;npm&lt;/code&gt; or your mirror of it). This affects your development experience.&lt;/p&gt;
&lt;p&gt;Moreover, package creep can introduce serious issues like security vulnerabilities, as you don’t own the code that resides inside the packages.&lt;/p&gt;
&lt;p&gt;Let’s jump into how to minimize the dependencies in your Node.js code.&lt;/p&gt;
&lt;h2 id=&quot;method-one-check-the-age-of-nodejs-packages&quot;&gt;Method One: Check the Age of Node.js Packages&lt;/h2&gt;
&lt;p&gt;Imagine a situation where you have a function that looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; findSomethingByIds&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;ids&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function’s purpose is to either find a single entity or a list of entities by their id. It’s common practice with different repository patterns.&lt;/p&gt;
&lt;p&gt;However, let’s say you want to know whether you’ve requested a single entity or a list of entities. A quick npm search leads us to &lt;a href=&quot;https://www.npmjs.com/package/isarray&quot;&gt;npm/isarray&lt;/a&gt;, an insanely popular package with over 62 million weekly downloads! You know the drill now:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; isarray&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t forget:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -D&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; @types/isarray&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But wait! Before you do that, have you noticed that this package is &lt;strong&gt;three years old&lt;/strong&gt;? And if you do a better search, you will discover that &lt;code&gt;isArray&lt;/code&gt; is now a part of the JavaScript core, can be invoked using &lt;code&gt;Array.isArray&lt;/code&gt;, and is perfectly supported in Node.js version 10 and up.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray&quot;&gt;Read more about &lt;code&gt;Array.isArray&lt;/code&gt; in Mozilla MDN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Package age is a good first indicator. Many old (2+ years) packages might have security vulnerabilities or be outdated. Some of the outdated packages are also merged into the official JavaScript spec.&lt;/p&gt;
&lt;p&gt;Another example is the &lt;a href=&quot;https://www.npmjs.com/package/trim&quot;&gt;trim&lt;/a&gt; package. It has more than 4 million downloads, and while it’s not that old (only 1), a native solution exists in JavaScript core: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim&quot;&gt;String.prototype.trim()&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So always try to find a native solution first, as JavaScript evolves fast and its standard library is always expanding. Don’t be fooled by the &lt;em&gt;weekly downloads&lt;/em&gt; counter in npm.&lt;/p&gt;
&lt;h2 id=&quot;method-two-using-one-liner-nodejs-packages&quot;&gt;Method Two: Using ‘One-liner’ Node.js Packages&lt;/h2&gt;
&lt;p&gt;Many ‘simple’ packages are actually what I call ‘one-liners’. A one-liner is a package that contains very few lines of code.&lt;/p&gt;
&lt;p&gt;If we continue with our &lt;code&gt;isArray&lt;/code&gt; example from above, by examining the content of &lt;code&gt;index.js&lt;/code&gt; we can see that it’s a simple one-line function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; toString&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {}.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  Array&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isArray&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;arr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; toString&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;call&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;arr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;[object Array]&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/juliangruber/isarray/blob/master/index.js&quot;&gt;Source: GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I always recommend you look at the source code of packages, at least simple ones because they can teach you a lot. Looking at the above source code, we discover one important thing: that &lt;code&gt;Array.isArray&lt;/code&gt; exists (the same conclusion we came to with method one).&lt;/p&gt;
&lt;p&gt;However, even if we did not have a native is-array method, the entire function is a simple if statement. And instead of requiring this package as our dependency, we can simply write the code as part of our project. It’s worth stopping for a minute and discussing the pros and cons of such an approach.&lt;/p&gt;
&lt;h3 id=&quot;using-npm-vs-writing-your-own-code&quot;&gt;Using Npm Vs. Writing Your Own Code&lt;/h3&gt;
&lt;p&gt;Npm is a public registry, and the code contributed to npm comes from people worldwide. It is amazing that we have such a big repository of free and open-source code. However, this also comes with some disadvantages.&lt;/p&gt;
&lt;p&gt;Npm, unfortunately, is known to be at high risk of attack. Packages might get compromised, whether by third parties or by developers themselves.&lt;/p&gt;
&lt;p&gt;We all remember the scandal around &lt;a href=&quot;https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/&quot;&gt;&lt;code&gt;left-pad&lt;/code&gt;&lt;/a&gt; and the recent rise in crypto-mining and crypto-stealing code that resides inside popular npm packages.&lt;/p&gt;
&lt;p&gt;While it’s impossible to audit every single package we install, we can lower the attack vector by using fewer packages in our projects. One great way to reduce the amount of packages we depend on is to write trivial packages ourselves.&lt;/p&gt;
&lt;p&gt;On the other hand, by writing some code ourselves, we deprive ourselves of the community’s sheer knowledge. Multiple people maintain popular packages, so they can quickly react to new vulnerabilities (for example, by monitoring the GitHub issues page of their package). This is something you, as a sole developer or part of a small organization, might lack the resources to do.&lt;/p&gt;
&lt;p&gt;So the next time you are eager to install a package, check its source code. It might be a one-liner you can write instead of introducing a potential attack risk and slowing your CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;But don’t go too far with this method: you don’t want to reinvent the wheel or entirely deprive yourself of the community’s support.&lt;/p&gt;
&lt;h2 id=&quot;method-three-extracting-sub-packages-with-lodash&quot;&gt;Method Three: Extracting Sub-packages with &lt;code&gt;lodash&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Imagine we have the following object interface:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; SomeObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  foo&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    bar&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      baz&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we have a function that accepts an object with that interface.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getBazOrDefault&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SomeObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you’ve guessed from the function’s name, it will give us the value of &lt;code&gt;baz&lt;/code&gt;, or &lt;code&gt;defaultValue&lt;/code&gt;, if the path to &lt;code&gt;baz&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;. Here is one implementation of that function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getBazOrDefault&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SomeObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;foo&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;foo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;bar&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;foo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;bar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;baz&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;foo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;bar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;baz&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ugly, right? It will get more ugly if you need to deal with arrays. Luckily, we can use the popular &lt;a href=&quot;https://www.npmjs.com/package/lodash&quot;&gt;&lt;code&gt;lodash&lt;/code&gt;&lt;/a&gt; library!&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;lodash&lt;/code&gt;, and the code becomes nice and easy to read:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; _&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;lodash&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getBazOrDefault&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SomeObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; _&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;foo.bar.baz&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Neat!&lt;/p&gt;
&lt;p&gt;However, if we install &lt;code&gt;lodash&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; lodash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And its type definitions (because we use TypeScript):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -D&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; @types/lodash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will introduce 8.4MB of dependencies to our &lt;code&gt;node_modules&lt;/code&gt;. That’s a lot!&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;❯&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; du&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -sh&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;8.4M&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    node_modules&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have some experience now, so let’s put it to practice!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Package age&lt;/strong&gt; — &lt;code&gt;lodash&lt;/code&gt; is relatively old - it was published a year ago. I couldn’t find any JavaScript core functionality to get a nested value from an object by a string key. Let’s move on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One-liner package&lt;/strong&gt; — &lt;code&gt;lodash&lt;/code&gt; is not a one-liner. It has tens of files and lots of tests. Even looking at the functionality of &lt;a href=&quot;https://github.com/lodash/lodash/blob/master/get.js&quot;&gt;&lt;code&gt;_.get&lt;/code&gt;&lt;/a&gt;, it’s not exactly a one-liner. While it’s a simple two-line function, it has an internal dependency on &lt;code&gt;./internals/baseGet.js&lt;/code&gt;, which in turn depends on &lt;code&gt;./castPath.js&lt;/code&gt; and &lt;code&gt;./toKey.js&lt;/code&gt; - and each depends on more files! It’s too much to be a candidate for extracting standalone code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So it seems we are stuck with &lt;code&gt;lodash&lt;/code&gt; then. But wait! There is another trick I want to show you! &lt;strong&gt;Sub-package extraction&lt;/strong&gt;. If we read the &lt;code&gt;lodash&lt;/code&gt; readme carefully, we will notice that every single functionality &lt;code&gt;lodash&lt;/code&gt; provides is extracted into its own &lt;a href=&quot;https://www.npmjs.com/search?q=keywords:lodash-modularized&quot;&gt;sub-package&lt;/a&gt;, including the &lt;code&gt;_.get&lt;/code&gt; function!&lt;/p&gt;
&lt;p&gt;Instead of installing the entire &lt;code&gt;lodash&lt;/code&gt;, we can install &lt;code&gt;lodash.get&lt;/code&gt; for just the &lt;code&gt;_.get&lt;/code&gt; function (of course, don’t forget the type definitions). This reduces the &lt;code&gt;node_modules&lt;/code&gt; size from 8.4MB to 3.6MB. It’s still a large folder, but a &lt;strong&gt;57%&lt;/strong&gt; reduction from the original size! I’ll happily take such a percentage reduction.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;❯&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; lodash.get&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;❯&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -D&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; @types/lodash.get&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;❯&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; du&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -sh&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;3.6M&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    node_modules&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;getBazOrDefault&lt;/code&gt; code will look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;lodash.get&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getBazOrDefault&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SomeObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;foo.bar.baz&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In conclusion, be aware that many packages, especially collections of utilities like &lt;code&gt;lodash&lt;/code&gt;, can be published to &lt;code&gt;npm&lt;/code&gt; as individual packages. Instead of installing the entire 312 different methods from &lt;code&gt;lodash&lt;/code&gt;, we can install the ones we actually need, reducing the weight of our &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;method-four-do-it-yourself&quot;&gt;Method Four: Do It Yourself&lt;/h2&gt;
&lt;p&gt;Last but not least is the DIY method.&lt;/p&gt;
&lt;p&gt;Let’s say we need a method to capitalize the first letter of each word in a given string. We can run an npm search for the term &lt;a href=&quot;https://www.npmjs.com/search?q=capitalize&quot;&gt;capitalize&lt;/a&gt; and be presented with 266 different packages (not all relevant).&lt;/p&gt;
&lt;p&gt;We can spend some time looking for a relevant, relatively maintained package and add it as a dependency to our project. Or we can write it ourselves!&lt;/p&gt;
&lt;p&gt;Why, you ask? Software engineering is the art of problem-solving. To become a great software engineer, you need to be able to solve software engineering problems.&lt;/p&gt;
&lt;p&gt;If you only know how to use third-party packages and copy code from Stack Overflow, you will be a software engineer, but not a great one. And you do want to be great, don’t you? Then let’s write our own capitalize function!&lt;/p&gt;
&lt;p&gt;Let’s start with a signature:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; capitalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, let’s identify words from our string. We can do that by splitting the string with whitespace:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need to capitalize the first letter for each word:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;charAt&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toUpperCase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;substring&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And lastly, we need to join the words back to the string:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; capitalizedString&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the entire function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; capitalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; str&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  for&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;charAt&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;toUpperCase&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;substring&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; words&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run this with some example strings:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;capitalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)); &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Hello World&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;capitalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;how are you?&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)); &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// How Are You?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great success!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - this function is for demonstration purposes only. It does not consider scenarios like quoted words - for example, &lt;code&gt;hello &quot;world&quot;&lt;/code&gt; will be capitalized incorrectly. However, this is a great opportunity to up your problem-solving skills! Go on and figure out how to capitalize quoted words as well!&lt;/p&gt;
&lt;p&gt;As with the second method, you’ll want to avoid ‘reinventing the wheel’ here. On the one hand, you don’t want to write the same functionality over and over. On the other hand, you don’t want to introduce a dependency for every small functionality you need.&lt;/p&gt;
&lt;p&gt;Always analyze and evaluate each solution. Many packages do way more than you need, so it’s better to implement the code yourself. However, there are good quality packages for common utils like &lt;a href=&quot;https://www.npmjs.com/package/slugify&quot;&gt;slugify&lt;/a&gt;, which is well-maintained and provides basic functionality you probably wouldn’t want to implement yourself in a production environment.&lt;/p&gt;
&lt;h2 id=&quot;wrap-up-decrease-your-dependencies-in-nodejs&quot;&gt;Wrap Up: Decrease your Dependencies in Node.js&lt;/h2&gt;
&lt;p&gt;In this post, we ran through four methods to analyze packages and shrink your &lt;code&gt;node_modules&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Checking the age of packages&lt;/li&gt;
&lt;li&gt;Using on-liner packages&lt;/li&gt;
&lt;li&gt;Extracting sub-packages&lt;/li&gt;
&lt;li&gt;Doing it yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These methods should make your development process faster, reducing the amount of packages you need to pull every time you run your CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;All in all, the &lt;code&gt;npm&lt;/code&gt; ecosystem is great! It is the biggest package registry as of today. But installing packages shouldn’t be a panacea. Our project is not only the code we write — it’s also the packages it consists of. Knowing what goes into our &lt;code&gt;node_modules&lt;/code&gt; makes our projects more resilient and makes us better developers.&lt;/p&gt;
&lt;p&gt;Until next time, happy coding!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This post was initially published in &lt;a href=&quot;https://blog.appsignal.com/2022/08/31/4-ways-to-minimize-your-dependencies-in-nodejs&quot;&gt;AppSignal Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Remote Work in The Netherlands - Part 2</title><link>https://kudmitry.com/articles/remote-work-in-the-netherlands-part-2/</link><guid isPermaLink="true">https://kudmitry.com/articles/remote-work-in-the-netherlands-part-2/</guid><pubDate>Tue, 01 Nov 2022 19:30:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a class=&quot;internal new&quot; href=&quot;/articles/remote-work-in-the-netherlands-part-1/&quot;&gt;Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Affected badly by the planned NS strike, with no place to go, we’ve mobilized ourselves to find a fast solution for our situation.
We tried to find a car rental service, but being located in Nunspeet, there were no car rental services nearby.
Public transport was not a solution as it would take too much time.
Our option was to find accommodation.&lt;/p&gt;
&lt;p&gt;No Airbnb were available nearby, but luckily for us, we found a hotel located 20 minutes away by walking, which is also pet friendly, and so we booked it.
NH Veluwe Sparrenhorst is part of the NH Hotel Group and is located right on the border of the Veluwe forest, which I told you about in my previous post.
We’ve booked the hotel for one night and went to sleep.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Our room in the NH Hotel Sparrenhorst&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1920&quot; height=&quot;1440&quot; src=&quot;/_astro/nh_hotel.Ca-YaiDW_2X2VV.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Our room in the NH Hotel Sparrenhorst&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Some of you might wonder why we did not stay one more night in our current Airbnb, and so I want to clarify this point for you.
We were tired, and a little stressed, but we did WhatsApp our host and asked for a quote for one more night.
He let us know that the property was available, however the price he has given us was more expensive than on Airbnb website.
Our minds immediately assumed he is trying to fool us and use our situation to extract more money, so we’ve ignored this option.
Later, after the hotel was already booked, we’ve realized that the price you see on Airbnb search page and the final price, &lt;strong&gt;are not&lt;/strong&gt; the same.
What we saw was the price on the search page, but once you try to book it, the price goes up and matches what our host offered us in WhatsApp.
I guess you learn by mistakes.&lt;/p&gt;
&lt;p&gt;After staying for one night in the hotel and working from there, we’ve hoped on a train and took the trip to our next destination.&lt;/p&gt;
&lt;h2 id=&quot;the-city-of-alkmaar&quot;&gt;The city of Alkmaar&lt;/h2&gt;
&lt;p&gt;Our next city, Alkmaar is almost the complete opposite of Nunspeet.
It’s a big city, with around 110k population.
It’s located about 30 km north of Amsterdam.
We were staying in a classical, 3 stories, Dutch house in a quiet neighborhood, a couple of minutes away from a ferry that takes you to the old and beautiful city center.&lt;/p&gt;
&lt;p&gt;In Alkmaar our life was more “normal”.
We were shopping at Albert Heijn for groceries, worked and traveled around the city.
We even were able to, accidentally, find our selves in Alkmaar’s cheese market.
Apparently, Alkmaar is popular for its cheese.&lt;/p&gt;
&lt;h2 id=&quot;the-road-home&quot;&gt;The road home&lt;/h2&gt;
&lt;p&gt;3 days before our flight, NS once again announced a nationwide strike.
We wanted to go to Delft and The Hague one day before leaving, but now with the announced strike it became a bit complicated.
We were also afraid that we might not be able to get to the airport due to the possibility that the strike might extend to more than one day, or any other logistic problems due to the consequences of the strike, as our flight was leaving in the morning.&lt;/p&gt;
&lt;p&gt;This time we’ve decided to rent a car for one day and return it in the airport.
We’ve rented a car from Sixt and drove to Delft and then to The Hague to visit some friends.
The next day, we’ve returned the car in the Airport and boarded our flight home.&lt;/p&gt;
&lt;p&gt;This concludes our first experience as Digital Nomads in a beautiful country of The Netherlands.&lt;/p&gt;
&lt;h2 id=&quot;cost-breakdown&quot;&gt;Cost breakdown&lt;/h2&gt;
&lt;p&gt;The idea of this part is to help you understand how much you can expect to pay for common things like accommodation and groceries, therefor I’m not going to include in this section things like flight tickets (which are personal to you depending on your flight route) or any personal spending we’ve had.&lt;/p&gt;
&lt;h3 id=&quot;accommodation&quot;&gt;Accommodation&lt;/h3&gt;
&lt;p&gt;The house in Nunspeet costed us approximately 2400 EUR.
In Alkmaar, we’ve paid approximately 850 EUR.
We did pay another 145 EUR for the hotel.&lt;/p&gt;
&lt;p&gt;The total expense for accommodations were approximately: &lt;strong&gt;3395 EUR&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;groceries&quot;&gt;Groceries&lt;/h3&gt;
&lt;p&gt;We bought our groceries mainly in Albert Heijn, with occasional visits to Lidl or Jumbo.
We found AH to be better quality.
I’m not going to include restaurants here, as this might differ from person to person and has little to do with the estimation of cost of living.
I will note that 90% of the time we cooked at home, and only when we were away from home, such as in a different city or traveling, we were eating out.&lt;/p&gt;
&lt;p&gt;Our total expense for groceries was: &lt;strong&gt;774.78 EUR&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;transportation&quot;&gt;Transportation&lt;/h3&gt;
&lt;p&gt;We’ve paid &lt;strong&gt;352.9 EUR&lt;/strong&gt; for trains, &lt;strong&gt;10 EUR&lt;/strong&gt; for a taxi from the hotel to the train station, and another &lt;strong&gt;5 EUR&lt;/strong&gt; for bus tickets.
A rental car for one day costed us &lt;strong&gt;161 EUR&lt;/strong&gt; and &lt;strong&gt;26.77 EUR&lt;/strong&gt; for extra kilometers range.
The fuel costed &lt;strong&gt;27.25 EUR&lt;/strong&gt;.
Two rental bicycles for two weeks costed us &lt;strong&gt;270 EUR&lt;/strong&gt; and another &lt;strong&gt;20 EUR&lt;/strong&gt; for one pannier.&lt;/p&gt;
&lt;p&gt;The total cost of transportation, was &lt;strong&gt;872.92 EUR&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;After the trip ended, I’ve learned that you can get an anonymous OV-chipkaart that costs 7.5 EUR.
The benefit of an anonymous travel card is that every time you buy a paper ticket, in addition to the ticket price, you also pay 1 EUR for the single-use chipkaart inside the ticket.
This could save you some money if you travel a lot using trains.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;misc&quot;&gt;Misc&lt;/h3&gt;
&lt;p&gt;We’ve paid &lt;strong&gt;77 EUR&lt;/strong&gt; for dog food as well as &lt;strong&gt;55 EUR&lt;/strong&gt; for tick removal.
The co-working I’ve mentioned in previous part costed us &lt;strong&gt;62 EUR&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>Remote Work in The Netherlands - Part 1</title><link>https://kudmitry.com/articles/remote-work-in-the-netherlands-part-1/</link><guid isPermaLink="true">https://kudmitry.com/articles/remote-work-in-the-netherlands-part-1/</guid><pubDate>Tue, 30 Aug 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the middle of August, my wife and I packed our suitcase and MacBooks, took our dog, and flew for a month to The Netherlands.
Luckily — we are working in companies with remote work policies.
Here’s my experience.
We landed at Schiphol airport and took a train via Amersfoort Central towards Nunspeet which became our hometown for the next two and a half weeks.&lt;/p&gt;
&lt;h2 id=&quot;why-the-netherlands&quot;&gt;Why The Netherlands?&lt;/h2&gt;
&lt;p&gt;Europe, and especially The Netherlands, is not the first choice for Digital Nomads.
The country is very expensive.
I’ll do a detailed cost breakdown in the next part, so stay tuned.&lt;/p&gt;
&lt;p&gt;Why The Netherlands?
We see it as a potential relocation destination, so we’ve wanted to see how it is to live a “normal” life there.
Sometimes, the entire life is not enough to understand if you like your country or not, let alone one month, but we’ve decided to do it anyway.&lt;/p&gt;
&lt;h2 id=&quot;the-town-of-nunspeet&quot;&gt;The town of Nunspeet&lt;/h2&gt;
&lt;p&gt;When we arrived at the border control at Schiphol airport, the officer asked us about the purpose and length of our trip.
When we said that we are going to stay there for a month, he was curious about where we are going to stay, so we replied: “&lt;em&gt;Nunspeet&lt;/em&gt;”.
He laughed and asked surprisingly: “&lt;em&gt;Nunspeet?! Why Nunspeet?!&lt;/em&gt;”.
This interaction summarized Nunspeet pretty well.&lt;/p&gt;
&lt;p&gt;Nunspeet is a town in the central Netherlands with a population of 27481 people (as of Jan 2019).
However, Nunspeet wasn’t our first choice.
We wanted to experience this mini relocation in two ways: outside the city and inside the city.
Our initial location was Zwolle — a much bigger city in the Northern Netherlands.
In Zwolle, we’ve booked a house almost on the city outskirt, away from the city life.
But due to unfortunate circumstances, our host had to cancel the booking.
And hence with less than a month before the trip, somewhat limited by our dog (not a lot of hosts will agree for a pet) — we’ve had to search for an alternative.&lt;/p&gt;
&lt;p&gt;And so we’ve found a lovely house in Nunspeet.
Coincidently, the distance between Nunspeet and Zwolle is 30km.
It’s a detached unit, in a community of around 20 similar units.
Some owners use them as permanent living, others rent them out.
It’s located around 10 minutes by foot from Nunspeet Train Station or about 2 minutes by bicycle.
Nunspeet Outdoor Center is conveniently located just by the train station, and we’ve used it to rent our bicycles for 2 weeks.&lt;/p&gt;
&lt;p&gt;But the coolest feature of it all is that it’s located in Veluwe — an 1100 km&lt;sup&gt;2&lt;/sup&gt; forest-rich ridge of hills.
And our house is located across the road from a forest in which we’ve spent many mornings and afternoons (at night it’s scary).&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Squerrel outside of our home in Nunspeet&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2000&quot; height=&quot;1334&quot; src=&quot;/_astro/squerrel.B9cDrdBQ_Z1mMSSY.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Squerrel outside of our home in Nunspeet&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;semi-cashless-work-life-balanced-society&quot;&gt;Semi-Cashless, work-life balanced society&lt;/h2&gt;
&lt;p&gt;We arrived on Saturday evening, and after unpacking and taking a shower, went to sleep.
We did buy some groceries at the airport for breakfast but planned to buy food the next day — on Sunday.&lt;/p&gt;
&lt;p&gt;In Europe, Sunday is considered to be the holy rest day but many businesses are still open and operating (albeit with a shorter opening hour).
Being located in a small town, rather than a big city, we’ve realized that Sunday in Nunspeet means no shopping.
Some businesses are open, like the Outdoor Center in which we’ve rented bicycles, and some restaurants, but grocery stores and other shops are closed.
The closest open Albert Heijn (a local grocery store chain) was in the city of Harderwijk.
Just 35 minutes by bicycle!
Without a second thought, we jumped on our bicycles and cycled through the beautiful countryside of the Netherlands.&lt;/p&gt;
&lt;p&gt;After buying some groceries and getting ready to head back, we’ve been surprised we can’t pay with a credit card.
Luckily an ATM was right inside the store, so we were able to withdraw some cash.
This phenomenon later haunted us through most of our stay in Nunspeet.
Most small stores refused to accept credit cards and operated mainly on cash or debit cards.
One business owner told us that credit card companies charge big fees, and so they refuse to support big financial institutions.
Understandable I guess.
However, it’s still a mystery to me why Albert Heijn, a big chain store, does not accept credit cards, even in big cities such as Utrecht.&lt;/p&gt;
&lt;p&gt;Debit cards are accepted everywhere though.
Some grocery stores such as Jumbo and Lidl, as well as almost all the restaurants — did accept credit cards though, so I still can’t figure out how they decide whether to accept credit-cards or not.&lt;/p&gt;
&lt;h2 id=&quot;utrecht-amsterdam-and-lots-of-cycling&quot;&gt;Utrecht, Amsterdam, and lots of cycling&lt;/h2&gt;
&lt;p&gt;After a week of working from suboptimal working conditions (couch for my wife and a dining table for myself), I’ve decided to explore local co-working options.
Mindspace was the biggest and the only one that allowed us to pay per day as well as make an online reservation (I hate phone calls), so we booked ourselves a table and headed to Utrecht.
An hour ride by train, not the closest city, and I wouldn’t like to take this ride every day.
A co-working was however a fresh restart and a change.
We were able to be more work-focused due to the office-like environment, as well as combine it with city exploring when we went for lunch as well as dinner afterward.&lt;/p&gt;
&lt;p&gt;On Friday we’ve decided to bike.
I think the Netherlands is the most bicycle-friendly country in the world.
We were able to bike for 60 km, &lt;strong&gt;all on bicycle lanes&lt;/strong&gt;, through more than 4 cities to arrive at a beautiful wildlife park.
We didn’t see many wild animals there, but the bicycle ride and the park itself were amazing.&lt;/p&gt;
&lt;p&gt;On Saturday we went to Amsterdam.
I’ve been to Amsterdam 3 times: one when interviewing for Booking.com; another one when we did bicycle touring with my friends, we stayed at a campsite near Amsterdam for 3 days; and the last time I flew to my wife when she had a connection there, so we’ve stayed there for 3 days.
I always loved Amsterdam.
It’s such an amazing city.
It’s uniquely antique but very modern and liberal.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Amsterdam - always beautiful&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2000&quot; height=&quot;1334&quot; src=&quot;/_astro/amsterdam.BjqSnD23_21ipOm.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Amsterdam - always beautiful&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;This time, I felt anxious in Amsterdam.
There were many tourists and a clear sign of people who come here only for weed and sex.
The city center was packed with people, and relatively dirty.
On the other side, once you take a few steps to the adjacent street, you fall in love with Amsterdam again.
I did some shopping there and spent the rest of the day in the beautiful Vondelpark.&lt;/p&gt;
&lt;p&gt;This concludes our first week there.&lt;/p&gt;
&lt;h2 id=&quot;got-sick-and-then-traveled-more&quot;&gt;Got sick and then traveled more&lt;/h2&gt;
&lt;p&gt;The second week started with me getting sick.
I thought it might be COVID, but the antigen test showed negative, so I assume it was the common cold and probably exhaustion.
I’ve spent most of the week in bed, sleeping, while my wife focused mainly on work.&lt;/p&gt;
&lt;p&gt;By the end of the week, I was getting better and was able to get back to work, and during the weekend, we decided to travel some more!&lt;/p&gt;
&lt;h3 id=&quot;zwolle-giethoorn-and-the-not-so-bicycle-friendly-country&quot;&gt;Zwolle, Giethoorn, and the not-so bicycle-friendly country&lt;/h3&gt;
&lt;p&gt;Remember I told you that Nunspeet was not our first option and that we initially wanted to stay on the outskirts of Zwolle?
Well, since Zwolle was 20-min ride by train, we decided to visit it.
Zwolle is a beautiful, big city.
It has a very cute city center.
We spent there half a day just exploring and doing some shopping.&lt;/p&gt;
&lt;p&gt;On Saturday we went to visit Giethoorn.
Giethoorn is a mostly car-free village and was very long on my list of places to visit.
I first heard about Giethoorn in 2015 when my friends and I did a two-week bicycle tour around The Netherlands.
We were throwing points of interest on a map and that’s how I’ve heard about Giethoorn.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Giethoorn&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2000&quot; height=&quot;1500&quot; src=&quot;/_astro/giethoorn.B_Ixxf1b_1UomQO.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Giethoorn&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;After visiting Giethoorn I must say I have mixed feelings.
While it’s advertised as a mostly car-free village, I must say that only a small part of it fits this description.
Mostly, it looks the same as a regular town/village.
However, the car-free part is very beautiful, and we rented a boat for 2 hours and sailed around the village and the farmlands nearby.&lt;/p&gt;
&lt;p&gt;Getting to Giethoorn was a bit complicated.
From Nunspeet you usually take a train to Zwolle and then from Zwolle a train to either Meppel or Steenwijk.
From the latter two, you can take a bus.
But hey!
The Netherlands is a bicycle paradise so why not cycle?
It’s about 40-min cycle from Meppel; about 20-min from Steenwijk.
We planned to get to Steenwijk and then cycle.
And while the train ride to Zwolle was pleasant, Zwolle was super packed with people as it was Saturday, and the platform with the train to Steenwijk was very crowded, so we’ve been afraid that there will be no place for bicycles, so we took an alternative train that stops in Meppel.
From Meppel it took us around 1-hour cycle towards Giethoorn, which was a beautiful cycle.&lt;/p&gt;
&lt;p&gt;Since it’s Saturday, trains are less frequent than during the work week, so on our way back, we had a train once an hour.
We’ve arrived at Steenwijk and planned to take an Intercity (IC) train to Zwolle and then a sprinter train to Nunspeet.
When the IC train arrived, it already had 3 bicycles inside and the train personnel told us that we can’t board the train with bicycles — even though we bought the required bicycle tickets.
They’ve said leaving the bicycle in an improper place is dangerous, and we should wait for the next train.
The problem is that the next IC train is in one hour, but they’ve suggested instead to board a sprinter to Meppel which will arrive in about 20 minutes then have a 30-minute transfer to a sprinter for Zwolle and finally a sprinter to Nunspeet.&lt;/p&gt;
&lt;p&gt;It was very odd to me.
A country where cycling is so engraved in people’s lifestyles, is not able to provide a good way to transport bicycles on a train.
Sprinter trains are more forgiving since they are a bit more spacious than IC trains, so luckily the 3 sprinters we took had enough space for our bicycles, and after a long, long ride, we were home.&lt;/p&gt;
&lt;p&gt;We also visited Amersfoort later on, to celebrate our anniversary in a nice restaurant there.&lt;/p&gt;
&lt;h2 id=&quot;the-happiest-country-is-not-that-happy-after-all&quot;&gt;The happiest country is not that happy after all&lt;/h2&gt;
&lt;p&gt;The Netherlands consistently ranks in the top 5 happiest countries.
The other 4 are usually Nordic countries, so it was always a mystery to me as to why the Netherlands.
There are many different explanations like good social services, being closer to nature or even the fact you are physically active by cycling everywhere.&lt;/p&gt;
&lt;p&gt;But it turned out that even the happiest countries in the world — have problems.
Throughout 2 weeks there, we’ve learned that there is an ongoing farmers’ protest that started 3 years ago, in 2019!
We saw many haystacks with angry faces and slogans which we couldn’t understand because they were written in Dutch, but the word “&lt;em&gt;kaput&lt;/em&gt;” probably meant something bad.
We’ve observed cars with red bandanas — a sign of people’s support for the farmers.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img alt=&quot;Unhappy farmers&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1500&quot; height=&quot;2000&quot; src=&quot;/_astro/farmers.Dxt8wt2Z_Z1h4uKm.webp&quot; srcset=&quot;&quot;&gt;&lt;figcaption&gt;Unhappy farmers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;And as we were sitting, on the evening of 29th of August, ready to pack our things and the next day move to Alkmaar, a city in the province of North Holland, which was supposed to become our next home for the rest of this mini-relocation, we’ve observed that on the 30th of August there will be the biggest NS strike in the Netherlands.&lt;/p&gt;
&lt;h3 id=&quot;ns-strike&quot;&gt;NS Strike&lt;/h3&gt;
&lt;p&gt;NS, or Nederlandse Spoorwegen which is Dutch for “&lt;em&gt;Dutch Railways&lt;/em&gt;”, the biggest state-owned railway operator in the Netherlands, as well as the busiest railway in European Union, and the third largest in the world after Switzerland and Japan — said they were going on a strike.&lt;/p&gt;
&lt;p&gt;The strike aimed to increase NS workers’ salaries as well as pension contributions, and was ongoing for a few days already, but was limited mainly to one of many of the Netherlands regions.
However, the planned strike on the 30th was in Utrecht which is a major hub and operation center for NS, hence NS was not able to run &lt;strong&gt;any&lt;/strong&gt;… &lt;strong&gt;trains&lt;/strong&gt;… &lt;strong&gt;AT ALL&lt;/strong&gt;.
On previous strike days, NS was able to provide limited service outside the strike area, but this time they’ve said they were not able to find a way to provide service due to the importance of Utrecht in the railway system.&lt;/p&gt;
&lt;p&gt;And so, without any option to leave Nunspeet, except for 6 buses that will take us anywhere between 4 and a half to 6 hours of travel time, or a taxi ride that will cost like a round trip flight ticket from the Netherlands back home, we were forced to find a solution just a few hours before going to sleep.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This concludes the first part of the article. &lt;a class=&quot;internal new&quot; href=&quot;/articles/remote-work-in-the-netherlands-part-2/&quot;&gt;Read the second part&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Implementing Promisable setTimeout</title><link>https://kudmitry.com/articles/implementing-promisable-set-timeout/</link><guid isPermaLink="true">https://kudmitry.com/articles/implementing-promisable-set-timeout/</guid><pubDate>Thu, 25 Aug 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In a world controlled by the event loop, &lt;code&gt;setTimeout&lt;/code&gt; is the king of execution control. It allows you to schedule function calls to be executed later. However, it can’t do one thing - suspend the execution for a certain amount of time. Think of an equivalent to &lt;code&gt;sleep(ms)&lt;/code&gt; from different languages.&lt;/p&gt;
&lt;p&gt;Let us now explore how we can implement it.&lt;/p&gt;
&lt;h2 id=&quot;promisifying-settimeout&quot;&gt;Promisifying setTimeout&lt;/h2&gt;
&lt;p&gt;In order to make &lt;code&gt;setTimeout&lt;/code&gt; behave similar to &lt;code&gt;sleep&lt;/code&gt; we first need to promisify it. For that we will use the &lt;code&gt;Promise&lt;/code&gt; constructor.&lt;/p&gt;
&lt;p&gt;A short recap. &lt;code&gt;new Promise((resolve, reject) =&gt; {})&lt;/code&gt; accepts two arguments: a &lt;code&gt;resolve&lt;/code&gt; function that we need to call when the promise resolves successfully (we can also pass the resolution value); and a &lt;code&gt;reject&lt;/code&gt; callback that is called if the promise is rejected (we can also pass the rejection error).&lt;/p&gt;
&lt;p&gt;Let’s look at a promisified &lt;code&gt;setTimeout()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then can execute it like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;hello&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà! And if we remove the callback, we can get a basic &lt;code&gt;sleep&lt;/code&gt; function&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; sleep&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which can be used as promise chain with &lt;code&gt;.then()&lt;/code&gt; or as &lt;code&gt;await&lt;/code&gt;-able inside an &lt;code&gt;async&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;We now have a basic &lt;code&gt;sleep(ms)&lt;/code&gt; function. I could end the article here, but &lt;code&gt;setTimeout&lt;/code&gt; has some other cool functionality like the ability to be canceled. And so we can implement a cancelable sleep!&lt;/p&gt;
&lt;p&gt;Imagine the following scenario. You want to do some async call to… let’s say an external API. But if the response takes too long, you want to return some known value. This is easily achievable with our &lt;code&gt;setTimeoutPromise&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1500&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ....&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getValueFromAPI&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; KNOWN_VALUE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// do something with value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we will either get the value from the external API or if it takes too much time (over 1.5s in this example), we will return a hard-coded value.&lt;/p&gt;
&lt;p&gt;Ideally, after we’ve got the value, i.e. &lt;code&gt;Promise.any&lt;/code&gt; was resolved, we would like to cancel the calls to the API and as well as the timeout because we don’t want to have unneeded things in our event loop. Most API calls can be canceled using &lt;code&gt;AbortController&lt;/code&gt; (more on that later), but what about the &lt;code&gt;setTimeout()&lt;/code&gt;?&lt;/p&gt;
&lt;h2 id=&quot;cancelable-settimeout&quot;&gt;Cancelable setTimeout&lt;/h2&gt;
&lt;p&gt;Not everyone knows, but &lt;code&gt;setTimeout&lt;/code&gt; actually returns a value.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The returned &lt;code&gt;timeoutID&lt;/code&gt; is a positive integer value which identifies the timer created by the call to &lt;code&gt;setTimeout()&lt;/code&gt;. This value can be passed to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout&quot; title=&quot;clearTimeout()&quot;&gt;&lt;code&gt;clearTimeout()&lt;/code&gt;&lt;/a&gt; to cancel the timeout.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— &lt;cite&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/setTimeout&quot;&gt;MDN#setTimeout&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;So we get a unique &lt;code&gt;timeoutID&lt;/code&gt; that can be used to cancel the timeout. It’s all nice and easy when the code is synchronous:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; timeoutId&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;doSomeWorkLater&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1500&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ... some more code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;clearTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;timeoutId&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How can we cancel a promiseable &lt;code&gt;setTimeout&lt;/code&gt;? Well, our &lt;code&gt;setTimeoutPromise&lt;/code&gt; function, instead of returning a promise function, can return an object that will contain two keys: the promise itself and a function that we can call to cancel the timeout, like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ReturnValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  timeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  cancel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ReturnValue&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; timeoutId&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; timeout&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    timeoutId&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    timeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    cancel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; timeoutId&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; clearTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;timeoutId&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our then previous example turns into something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1500&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ....&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;timeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cancel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; KNOWN_VALUE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getValueFromAPI&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;timeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cancel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// do something with value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;but-what-about-abortcontroller&quot;&gt;But what about &lt;code&gt;AbortController&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;I briefly mentioned &lt;code&gt;AbortController&lt;/code&gt;, but for those who are not familiar with it, it’s a mechanism to abort web requests. It became a standard way to abort requests made with the &lt;code&gt;request&lt;/code&gt; library and was adopted by &lt;code&gt;axios&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;In a nutshell, &lt;code&gt;AbortController&lt;/code&gt; consists of two parts: the controller itself and a signal known as &lt;code&gt;AbortSignal&lt;/code&gt;. The signal is given to the abortable targets while the controller remains in the hands of the one who wishes to abort the request.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; controller&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; AbortController&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; signal&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; controller&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;doSomeAsyncWork&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;abort&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be nice to rewrite our &lt;code&gt;setTimeoutPromise&lt;/code&gt; to use the &lt;code&gt;AbortController&lt;/code&gt; instead of the inconvenient &lt;code&gt;cancel&lt;/code&gt; method we need to deal with.&lt;/p&gt;
&lt;h3 id=&quot;set-timeout-but-with-abortcontroller&quot;&gt;Set timeout but with &lt;code&gt;AbortController&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;AbortController&lt;/code&gt; itself exposes nothing except for the &lt;code&gt;signal&lt;/code&gt; and the &lt;code&gt;abort&lt;/code&gt; method. Remember that we’ve said it’s just a tool to signal for abortion. Therefore we need to look at the signal itself, namely the &lt;code&gt;AbortSignal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we look at the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal&quot;&gt;AbortSignal&lt;/a&gt; we can see it has two read-only properties: a boolean &lt;code&gt;aborted&lt;/code&gt; to indicate if the signal was aborted, and &lt;code&gt;reason&lt;/code&gt; which can be any value to indicate the reason the signal was aborted (it’s taken from the first argument passed to &lt;code&gt;controller.abort(reason)&lt;/code&gt;). It also has a method &lt;code&gt;throwIfAborted()&lt;/code&gt; which throws the &lt;code&gt;reason&lt;/code&gt;. I suspect it’s just a shorthand for&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; throwIfAborted&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;aborted&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;None of them are helpful for what we need. Luckily for us, &lt;code&gt;AbortSignal&lt;/code&gt; is also an &lt;code&gt;EventTarget&lt;/code&gt; which means it can listen to events. One specific event that we are interested in - is the &lt;code&gt;abort&lt;/code&gt; event. And with the help of &lt;code&gt;addEventListener()&lt;/code&gt; method provided by the &lt;code&gt;EventTarget&lt;/code&gt; - we can actually implement our &lt;code&gt;setTimeoutPromise&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;First, we need to make sure our function is able to accept the &lt;code&gt;AbortSignal&lt;/code&gt;. The usual convention with &lt;code&gt;AbortSignal&lt;/code&gt; is not to pass it as a standalone argument but as part of &lt;code&gt;options&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s define it&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Options&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  signal&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AbortSignal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;  ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  { &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Options&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // logic here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We still need to return a promise. Also, since it is possible to pass an already aborted signal, we need to have some edge case checking here&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;aborted&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Aborted&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, if the signal was not aborted, we need to create the timeout and capture its id&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; timeoutId&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, what’s left is to subscribe to the &lt;code&gt;abort&lt;/code&gt; event of our signal, clear the timeout and reject the promise&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;abort&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  clearTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;timeoutId&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Aborted&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final result looks like this&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Options&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  signal&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;AbortSignal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;  ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  { &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Options&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;aborted&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Aborted&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; timeoutId&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()), &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;abort&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      clearTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;timeoutId&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      reject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Aborted&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can rewrite our example from earlier like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1500&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ....&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; controller&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; AbortController&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getValueFromAPI&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  setTimeoutPromise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; KNOWN_VALUE&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;signal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;abort&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// do something with value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence, no matter what promise resolves first, after we get the value, we can abort the execution of all the remaining promises.&lt;/p&gt;
&lt;p&gt;And so, we’ve created our very own, promisified, cancelable version of &lt;code&gt;setTimeout&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;but-you-dont-need-all-this&quot;&gt;But… You don’t need all this&lt;/h2&gt;
&lt;p&gt;NodeJS v16 introduced &lt;a href=&quot;https://nodejs.org/docs/latest-v16.x/api/timers.html#timers-promises-api&quot;&gt;Timer Promises API&lt;/a&gt; which are accessible under the &lt;code&gt;timers/promises&lt;/code&gt; module. So you can get a promisified version of all the different timers such as &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setImmediate&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;timers/promises&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; val&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// prints &apos;hello world&apos; after 1s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And they even accept &lt;code&gt;signal&lt;/code&gt; as the last argument to control their cancellation.&lt;/p&gt;
&lt;h3 id=&quot;outro&quot;&gt;Outro&lt;/h3&gt;
&lt;p&gt;You might think “&lt;em&gt;well if it’s available natively in NodeJS, why do I need to implement this myself?&lt;/em&gt;”. And you are right - you should not. If you need timers with promises, use them from &lt;code&gt;timers/promises&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;, I’m a big believer in learning through understanding how things work. Most of the knowledge I’ve acquired is because I was curious to know how things work: how operating system kernel works; how IRC protocol works; how instant messengers work; how a compiler works; etc.&lt;/p&gt;
&lt;p&gt;When you take an existing, working solution, and try to reimplement it - you learn a lot about why and how decisions were made. And this makes you a better developer.&lt;/p&gt;
&lt;p&gt;This is the first article in a series of &lt;a href=&quot;/tag/understanding-implementations&quot;&gt;Understanding Implementations&lt;/a&gt;, where I’m going to uncover how things are implemented, in order to help you become better developers. If you have suggestions on what other topics I should cover in this series, feel free to drop me an Email. Until next time!&lt;/p&gt;</content:encoded></item><item><title>The Future is Grim. Maybe.</title><link>https://kudmitry.com/articles/the-future-is-grim-maybe/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-future-is-grim-maybe/</guid><pubDate>Fri, 17 Jun 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About a week ago, I met with one of my friends for beer. We catched up on each other’s lives, spoke about current day events - mainly the &lt;a href=&quot;https://war.ukraine.ua/&quot;&gt;War in Ukraine&lt;/a&gt;, the recent crash in the stock market, and other events happening right now in the world. At some point I’ve said “&lt;em&gt;Everything looks so fucked up&lt;/em&gt;”, and he added “&lt;em&gt;Yeah…&lt;/em&gt;”.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;After a few moments of silence, I asked him “&lt;em&gt;Do you think our parents had similar feelings about their lives and their future?&lt;/em&gt;”. My friend is a popular blogger and has a big community around him, and he went on telling me that just recently he ran a survey among his community on the exact same question. We were both born in Post Soviet Union countries and most of his community are members from post USSR, so he asked them whether their parents had a feeling that their lives and future is totally fucked up. And surprisingly, their parents’ answers were - no. They didn’t have that feeling.&lt;/p&gt;
&lt;h1 id=&quot;a-brief-history-of-ussr&quot;&gt;A brief history of USSR&lt;/h1&gt;
&lt;p&gt;Most of our parents were born somewhere around 1950-1960. A mere decade after the end of the second world war. The USSR had lost 13.7% of its population, a devastating 26,600,000, due to WW2. You don’t recover from that in one decade.&lt;/p&gt;
&lt;p&gt;On top of all that, Germany and Berlin were divided among the winning allied powers, the most notable being the US and USSR, with a completely different world views and societal order - Capitalism vs Communism. This led to yet another war - The Cold War which lasted almost 5 decades. The US was spreading Capitalism all over the world, while the USSR was hidden behind an Iron Curtain manifesting its communist views to its citizens.&lt;/p&gt;
&lt;p&gt;The peak point of the Cold War was the Cuban Missile Crisis - a 35-day confrontation that could have brought the world and human civilization to an end with Nuclear War.&lt;/p&gt;
&lt;p&gt;Then in 1968 Soviet and Warsaw Pact troops invaded Czechoslovakia. In 1969 there was a clash between Soviet and Chinese troops across the border. Then things were silent for a few decades, up until 1986 when the Chernobyl nuclear power station exploded.&lt;/p&gt;
&lt;p&gt;1989 signals the beginning of “Revolutions of 1989” leading to toppling of Soviet imposed communist regimes, the Fall of the Berlin Wall and eventually the Fall of USSR in 1991.&lt;/p&gt;
&lt;p&gt;The fall of the USSR followed by very unsettling times and the rise of Anti-Semitism which forced a lot of people to immigrate. And the rest is modern history.&lt;/p&gt;
&lt;p&gt;50 years of tension between two world powers, a prospect of Nuclear Annihilation and Iron Curtain, and yet, our parents did not feel that everything is fucked up. Why is that?&lt;/p&gt;
&lt;h1 id=&quot;information&quot;&gt;Information&lt;/h1&gt;
&lt;p&gt;On 26 of April 1986 at the No.4 Nuclear Reactor of the Chernobyl Nuclear Power Plant - an explosion occurred. An explosion that released enormous amounts of radiation over Ukraine, Belarus and Russia and neighboring European countries. Only 36 hours after the event, the first news about evacuations started to appear in mass media. And on 28th of April, the Russian national federal news agency reported that an explosion occurred.&lt;/p&gt;
&lt;p&gt;An event that big was hidden for more than 2 days. This was in 1986. Also, in that year, IBM announced its first laptop computer - the IBM PC Convertible, weighing 12 pounds (~5.4 kg), which is 18 pounds (~8.1 kg) lighter than the earlier portable computer.&lt;/p&gt;
&lt;p&gt;Another nuclear disaster occurred on 11 of March 2011 at the Fukushima Daiichi Nuclear Power Plant in Japan. At 14:46 local time, an earthquake hit Honshu Island causing nuclear reactors 1, 2 and 3 to shut down automatically. At 15:46 a tsunami overtops the seawall that was designed to protect the plant and causes flooding and malfunction of the backup generator, which in turn leads to overheating.&lt;/p&gt;
&lt;p&gt;16:00 - The Nuclear and Industrial Safety Agency of Japan initiates an emergency headquarters to gather information. And at 19:03, a bit more than 4 hours from the start of the event, the prime minister Naoto Kan declares a nuclear emergency status. Also in 2011 Emojis became mainstream after their introduction on the iPhone.&lt;/p&gt;
&lt;p&gt;The way we access information today is way different from what it was in the USSR. Back in USSR times, you’d learn about events from the radio or newspapers. Radios were not portable so you rarely carried one in your pocket; and newspapers needed to be printed and distributed, so information became outdated by the time you read it.&lt;/p&gt;
&lt;p&gt;Moreover - radio air time was limited as well as the amount of news you could print in a single newspaper, so editors had to pick the most “interesting” or “shocking” news to publish. Individual press was nearly non-existing; and major news outlets, especially in the USSR, were controlled by the ruling party. So you get one point of view, censored by the ruling party.&lt;/p&gt;
&lt;p&gt;This is way different from today. Today you have a ton of independent, oftentimes first-hand reports of live events. YouTubers and TikTokers find themselves among the early ones to cover major events, way ahead of news agencies - which still rely on some sort of editing and publishing format.&lt;/p&gt;
&lt;p&gt;News are broadcasted via countless TV channels, while at the same time being mirrored to websites and YouTube streaming. On demand news is accessible on a by-minute basis through platforms like YouTube, Instagram, Facebook, TikTok and Telegram. Unfiltered, uncensored. All in one device, weighing somewhere around 6.1 ounce (~173 grams) that fits easily in your pocket, with a constant connection to the Internet.&lt;/p&gt;
&lt;h1 id=&quot;our-world-in-data&quot;&gt;Our world in data&lt;/h1&gt;
&lt;p&gt;I really like the website &lt;a href=&quot;https://ourworldindata.org/&quot;&gt;ourworldindata.org&lt;/a&gt;. They have 3280 charts across 297 topics including - Demographic, Health, Food, Living Conditions, etc.&lt;/p&gt;
&lt;p&gt;According to some of their research, all major metrics associated with happy life - are increasing. Life expectancy, for example, has grown from an average of 32 years in 1900, to  an average of 72 years in 2019 (&lt;a href=&quot;https://ourworldindata.org/human-development-index#health&quot;&gt;source&lt;/a&gt;). The share of battle deaths is declining (&lt;a href=&quot;https://ourworldindata.org/war-and-peace#the-share-of-battle-deaths-is-declining-even-faster&quot;&gt;source&lt;/a&gt;). Global education has increased as well (&lt;a href=&quot;https://ourworldindata.org/global-education&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;And yet, we feel like everything is doomed. Especially during the past 3 years. And the biggest reason that my friend and I believe is to blame - is social media and rapid access to information.&lt;/p&gt;
&lt;p&gt;Humans developed from hunter-gatherers in predator filled environments. Our brains are programmed to look for danger. If you spot the lion before it spots you - you get another day to live. We are addicted to “bad” news. Bad news helps us survive.&lt;/p&gt;
&lt;p&gt;Now - here is the catch. Evolution works well over time. A long time. Like a really long time. You won’t see substantial change in the evolution of our brain over one generation. Technology on the other side progresses superfast. The first transistor computers appeared in 1955. There are now teenagers who have never seen a Floppy Disk before.&lt;/p&gt;
&lt;p&gt;Technology has blessed us with many good things. But our brains did not evolve enough to adapt to technology, so we are still hooked on looking for disasters. Especially now - when disasters are one swipe away. Disasters and catastrophes sell. Nobody wants to read the news about peaceful pandas. We are hooked on gossip, and “fail” videos. We are motivated by a scarcity mindset that is being sold to us by marketers. “Beware! Only today! 19.99 instead of gazillion”.&lt;/p&gt;
&lt;p&gt;An entire generation is feeling hopeless, while the data shows that our world is becoming better, all due to rapid access to technology and by-minute coverage of major world events.&lt;/p&gt;</content:encoded></item><item><title>&quot;Good Article. 4 out of 5 starts&quot; or What is wrong with the star rating system</title><link>https://kudmitry.com/articles/what-is-wrong-with-the-start-rating-system/</link><guid isPermaLink="true">https://kudmitry.com/articles/what-is-wrong-with-the-start-rating-system/</guid><pubDate>Fri, 13 May 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Imagine you want to buy a new vacuum cleaner. You go to Amazon, search for vacuum cleaner, set some filtering options like: brand, price, color, shape, and you get… tens, if not hundreds, of results. How do you pick the one that satisfies you the most?&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Of course, the good old “star rating” system. It’s everywhere. TripAdvisor has it for restaurants and attraction; IMDB got it for movies; Amazon for consumer products; Goodreads for books. It’s the baseline by which we can compare one product to another.&lt;/p&gt;
&lt;p&gt;But the star rating system is not about the product, its more about the people.&lt;/p&gt;
&lt;h1 id=&quot;lets-start-with-a-question&quot;&gt;Let’s start with a question&lt;/h1&gt;
&lt;blockquote&gt;&lt;p&gt;Is product A with 2/5 stars, always worse than product B with 5/5 stars (assuming products A and B belongs to the same product category, for the sake of science behind my words)?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Well, the answer as always: It depends. If the same people who rated product A with 2 stars, rated product B with 5 stars — then yes, we can &lt;em&gt;assume&lt;/em&gt; that product B is better than A &lt;strong&gt;*to them*&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But very few of us have the hobby of purchasing two different products just for the sake of finding the best one. Yes there are people who get paid to do this, and they usually write articles which can be summarized in the following pseudo-quote:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Here is product A with its cons and pros. Here is product B with its cons and pros. Which one should you buy? Well that depends..&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So most of the time we find ourselves comparing product A with the rating of 2/5 which was given to it by 3629 people &lt;strong&gt;versus&lt;/strong&gt; product B with the rating of 4/5 which was given to it by 79 people. And while we can read 3–5 short reviews about each product we compare, it is almost impossible to read tens, hundreds or even thousands of reviews about each product we are comparing. This is why the star rating system was invented. We ain’t got no time to read those reviews. 2/5 versus 4/5? 4/5 is clearly better. Done.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/shutup.jpeg&quot; alt=&quot;Take up my money&quot;&gt;&lt;figcaption&gt;Take up my money&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1 id=&quot;but-wait-ive-got-a-story-to-tell&quot;&gt;But.. Wait! I’ve got a story to tell.&lt;/h1&gt;
&lt;p&gt;Every idea stars with a personal experience (or just pops up randomly while you are showering), and the idea of how broken the star rating system came to me very long ago when I was shopping for a beard trimmer. I had my budget set, I had my needs outlined, and I went to the battleground in the lands of the internet reviewers. I’ve read reviews, watched reviews, asked friends for opinions and eventually found the one!&lt;/p&gt;
&lt;p&gt;So I went to my local Amazon-like website to see where I can buy that awesome beard trimmer, that was the best of the best in its class based on my needs and budget, and I was shocked to see that it got only 3/5 stars!&lt;/p&gt;
&lt;p&gt;I was mentally broken. “How come?!” I wondered. “Why?!” I asked myself. “How is it possible that after hours of research, the best I can get is only 3/5 stars?!”. So I’ve read the reviews, there weren’t too many of them. I concentrated on the negative ones, because I already knew that my product was the best of the best, I wanted to know why some people found it to be bad. So there was one guy, who broke for me the concept of star rating system. His review went like this (I’m rephrasing, it was long time ago):&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Very good trimmer. Trims the beard nicely, battery time is long enough and the provided stand is good. Unfortunately can not be used as hair cutting machine. 2/5&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;“&lt;em&gt;Can not be used as hair cutting machine&lt;/em&gt;” — random guy on the internet about a beard trimmer.&lt;/p&gt;
&lt;p&gt;This was the most negative review, others were good. I was baffled for few minutes. Then went to the store and years after I still have this beard trimmer that still works perfectly.&lt;/p&gt;
&lt;h1 id=&quot;the-star-rating-system-is-broken&quot;&gt;The star rating system is broken&lt;/h1&gt;
&lt;p&gt;Humans like visual things. This is why we prefer PowerPoint presentations with pie charts and graphs as opposed to boring talks with numbers. And this is why we prefer 5/5 stars as opposed to reading 3681 reviews. And statistically, the more people rate the product higher — the more chances that we will be satisfied by it. But statistics works good on objective data, i.e. “&lt;em&gt;If you will be moving at 10km/h you will arrive in one hour, so in order to arrive in 30 minutes, you need to move 20km/h&lt;/em&gt;”, and works very bad on subjective data: “&lt;em&gt;This restaurant sucks. They have only spicy food, and I’m allergic to spicy food. 1/5&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;Of course such reviews are the minority, and we can treat them as measurement errors because they are subjective and influenced by the human factor: personal preferences, emotional state at the time of the review, having high expectations from the products prior to purchasing it etc. And the more people review the product, the less important those measurement error review becomes, because statistics! But this brings me to the first problem.&lt;/p&gt;
&lt;h2 id=&quot;1-most-reviews-are-subjective-and-simple&quot;&gt;1. Most reviews are subjective and simple&lt;/h2&gt;
&lt;p&gt;i.e. “&lt;em&gt;This vacuum cleaner is good, because it cleans the vacuum. Is it the best? No idea, this is the only one I’ve owned&lt;/em&gt;”. There is no objective data in this review. I don’t know the size of your apartment, how dirty it is, how frequently you use your vacuum cleaner etc. It is not possible to make a decision between 2 vacuum cleaners that way, because chances are low that the same group who reviewed the first one, also reviews the second one, therefor rendering the reviews useless.&lt;/p&gt;
&lt;p&gt;But honestly, you don’t have to make that decision. Chances are, if both of them are rated 5/5 by hundreds of people, and you’ve outlined your needs correctly, you will be satisfied with any of them. Assuming you will be able to avoid the afterthought of “did I choose the right one?”, which brings me to the second problem.&lt;/p&gt;
&lt;h2 id=&quot;2-lets-go-to-watch-the-moviebut-it-got-only-54-on-imdb&quot;&gt;2. Let’s go to watch “The Movie!” — “But it got only 5.4 on IMDB”&lt;/h2&gt;
&lt;p&gt;Star rating system ruins our experience. Ever checked the rating of the movie on IMDB prior to watching it? Or the rating of the book on Goodreads prior to reading it? If It’s low, you won’t like it, no matter how good it was. If the rating is high, you will come with high expectations and most likely will be disappointed, because the human anticipation is always bigger than the reality. Which in turns brings me to the third problem.&lt;/p&gt;
&lt;h2 id=&quot;3-what-does-5-stars-mean&quot;&gt;3. What does 5 stars mean?&lt;/h2&gt;
&lt;p&gt;When I tell you, that this restaurant is 1/5 stars, what do you imagine about it? That its dirty, the waiter takes hours to bring you your food, and when your food arrives its cold, not fresh, and by the time you finish eating it you want to puke and on top of all that, they make a mistake and charge your credit card 3 times with 25% tip even though you did not want to leave tip at all?!&lt;/p&gt;
&lt;p&gt;And what if I tell you its 5/5 stars? You imagine that the food is fresh, the waiter brings you your order even before you finished thinking about what you want to order, and they don’t charge you any money at all?&lt;/p&gt;
&lt;p&gt;We like to exaggerate our expectations. If its bad — it must be the absolute worst, and if its good — it must be the absolute best. There is no in between. The in between is reserved for 2, 3 and 4 stars. 1 is absolute worst, 5 is absolute best. But is it?&lt;/p&gt;
&lt;p&gt;For me, as a reviewer, it is very hard to give either 1 star or 5 stars on a 1-to-5 star scale, to a product / place / item, because I always have this question of “&lt;em&gt;1 star compared to what? Am I sure that this is the absolute worst vacuum cleaner I’ve ever tried? And what happens if the next one will be worse than this one, should I just give this one 2 stars and reserve 1 star for the absolute worst?&lt;/em&gt;”. Which in turns brings me to the fourth problem.&lt;/p&gt;
&lt;h2 id=&quot;4-reviews-are-damn-hard&quot;&gt;4. Reviews are damn hard&lt;/h2&gt;
&lt;p&gt;Reviewing things is a hard process. If you don’t do it professionally, It’s hard to avoid subjectivity in the review, in the end you’ve spent your time and your money therefor making bad decision about the particular product — painful and sometimes even expensive.&lt;/p&gt;
&lt;p&gt;Most of the reviews are written within a short period from purchase date of the product, and in that time you either experience good feelings from your new fresh product, and you unconsciously deny any negative facts; or the opposite, you are so dissatisfied with your product so you continue to search for the bad things in it and just want to bash it over the internet.&lt;/p&gt;
&lt;p&gt;But even if you were satisfied with the product, and you left a positive review, but then it suddenly broke after few months of careful usage, you just throw it away and buy a new one, any very few of us go back to their original review to alter it: “Was good, but broke pretty fast”, so the review stays there no longer providing the real picture. And star rating systems do not reflect that information. They only reflect the emotional state and satisfaction level of the individual person at the time of the review.&lt;/p&gt;
&lt;h2 id=&quot;to-sum-up&quot;&gt;To sum up&lt;/h2&gt;
&lt;p&gt;If you don’t have access to high volume market stores like Amazon, where products can get thousands or ten thousands amateur and professional reviews, you are most likely exposed to those “measurement error reviews” where four people are satisfied with their vacuum cleaner, and the fifth person expected it to make him coffee every morning so “&lt;em&gt;1/5&lt;/em&gt;”, thus no longer providing the real picture. In addition, people tend to ignore good things and concentrate on the bad ones, therefor if you are satisfied with the product, you rarely go and review it. But if it was terrible, boy you are going to bash about it in every possible corner of the internet because the company that made this piece of shit, must be burnt.&lt;/p&gt;
&lt;p&gt;Star rating system was introduced to help us as, consumers, make better choices. Whether it is what movie to watch, what book to read, what restaurant to visit, which hotel to stay in or what vacuum cleaner to buy. But the truth is that star rating systems are based on the subjectivity of each individual towards the product, rather than the objectivity of the product itself.&lt;/p&gt;
&lt;p&gt;And subjectivity of each individual towards a specific product or service, is not something that we can map onto a mathematical model because it depends on so many things: emotional state, personal preferences towards things like shape / color / size / manufacturer / taste / smell, the usage environment (robot vacuum cleaners are probably bad for very small, dense apartments for example), prior-to-purchase-expectations etc.&lt;/p&gt;
&lt;h1 id=&quot;so-what-can-be-done&quot;&gt;So what can be done?&lt;/h1&gt;
&lt;p&gt;Honestly? Nothing. Like many things in our world, it’s not perfect, but it works most of the time. Chances are that a product with the rating of 3.8/5 that was rated by 3780 people is better choice than a product with rating of 5 that was rated by one person. There are exceptions, in the end it’s all statistics and people have different needs and tastes.&lt;/p&gt;
&lt;p&gt;One possible solution is what YouTube does. If you remember, back in the days, YouTube had ratings for their videos, then they switched to Like / Dislike button. It is still based on statistics, but less prone to measurements errors. However, you know what they say:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The greater the number of people who find an idea correct, the more the idea will seem to be correct to us.&lt;/p&gt;&lt;p&gt;- Influence, the psychology of persuasion, Robert Cialdini&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Another possible solution would be to split the 5-star rating system into categories. Let’s continue with our vacuum cleaner examples. Instead of rating individual vacuum cleaner on a scale of 1 to 5, we can rate it in a scale of 1 to 5 in different categories: how noisy it is, how light it is, how compact it is, how good it cleans, how portable it is etc. Each category is still exposed to the problems I’ve outlined, but since you split the eggs to different baskets, there is less space for measurement errors.&lt;/p&gt;
&lt;p&gt;But the most important thing is to remember that sometimes it doesn’t really matter. If you are having difficultly to decide between a 4.5 and 4.8-star product, if the both have hundreds of reviewers, chances are, you will be satisfied with both.&lt;/p&gt;</content:encoded></item><item><title>Legacy is where companies go to die</title><link>https://kudmitry.com/articles/legacy-is-where-companies-go-to-die/</link><guid isPermaLink="true">https://kudmitry.com/articles/legacy-is-where-companies-go-to-die/</guid><pubDate>Sat, 09 Apr 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Legacy. The one word engineers cringe when they hear. Nobody wants to work with legacy code or legacy systems. And yet someone has to. Can we do something about it?&lt;/p&gt;
&lt;h2 id=&quot;the-evolution-of-software&quot;&gt;The evolution of software&lt;/h2&gt;
&lt;p&gt;Let’s look at the evolution of software. It all starts with an idea. After some market research and market fit, a small team is formed to develop an MVP. The MVP then gets a few real users, which sets the ball rolling for team expansion and accelerated feature development. And as new features are being shipped, old features are becoming what we call legacy, and the backlog in Jira is getting filled with Tech Debts that are never prioritized because &lt;em&gt;We must develop this feature ASAP!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Eventually, the code becomes very hard to maintain. Most of the original authors already left the company and new engineers with enthusiasm to make the code a better place are met with criticism such as&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This code is the core of the system, and any change there needs to be carefully reviewed by Amy’s team. But her team does not have time for it in this quarter.&lt;/li&gt;
&lt;li&gt;Nobody really knows what’s going on there. Dany was the last to work on it, but he now left for UniCo(rn) which raised gazillion dollars.&lt;/li&gt;
&lt;li&gt;Yeah, we can do that, but maybe in the next quarter. We really need you to work on Feature X for ThisBigClient.&lt;/li&gt;
&lt;li&gt;I’m in! Let’s create a ticket in Jira Backlog and prioritize it in our next grooming!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And hence, their passion and enthusiasm slowly dies, until one day they become the ones who say the above phrases.&lt;/p&gt;
&lt;p&gt;Legacy is the place where companies go to die.&lt;/p&gt;
&lt;h2 id=&quot;if-you-fear-legacy--you-are-doomed-to-fail&quot;&gt;If you fear legacy — you are doomed to fail&lt;/h2&gt;
&lt;p&gt;Code is just a means to produce products. The harsh truth is that your code quality doesn’t really matter, as long is it produces the product that your customers are happy to pay for. And in a very competitive world we live right now, you don’t really have time to produce maintainable code. New startups don’t have time or resources to focus on code quality — they need to provide POC as fast as possible to acquire that one customer. Established companies already have customers who give zero concerns about your legacy code, and are mainly interested in features.&lt;/p&gt;
&lt;p&gt;However, we are not in the 15th century, where you work as blacksmith your entire life, and the cost and time you will spend investing into your workshop will eventually pay for itself. In the modern world, people are not their companies. It’s very common for engineers to change jobs every 3 to 5 years. And investing in a “better code” will not benefit future you, because future you will work in a different company in few years. Same goes for your manager, his manager and your product manager. Even the most veteran people, who work for 10 or 15 years at the same place, will usually occupy a completely different position by the time you approach them to ask for help about the legacy they’ve written 9 years ago.&lt;/p&gt;
&lt;p&gt;Because good companies will usually outlive their entire employees, legacy creates a big problem for them. If you want your company to be good, you must have 2 magic components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ability for new employees to support and maintain your codebase without hunting for knowledge from veteran employees or those who already left&lt;/li&gt;
&lt;li&gt;Ability to innovate&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;foobaz-automobiles-inc&quot;&gt;FooBaz Automobiles Inc.&lt;/h3&gt;
&lt;p&gt;I like to provide analogies from other industries, and I’m somewhat enthusiastic about cars, so here is one. Let’s say you start a car company and you rush your small engineering team to develop an efficient engine. Once your engine has been developed and you’ve provided a first prototype of your car, you begin to work on customer requests: better seats, nicer dashboard, more safety features etc. However as time passes, your engine starts to show its age.&lt;/p&gt;
&lt;p&gt;Your new enthusiastic employee proposes to redesign the engine, but he is met with criticism: the current engine fits in the chassis we are developing; the engine is a critical part of the car; very few people know how the engine works and it’s better not to touch it; we promised our customers a big screen with navigation etc. As time passes, Acme Automobiles Inc is founded, and your enthusiastic employees are moving there, so they can innovate on their engine design, which proves to be twice as cheap and efficient as yours. And so little by little your company is phased out from existence.&lt;/p&gt;
&lt;p&gt;Legacy code is like a viral infection. If you leave it untreated, it will spread and infect your entire codebase.&lt;/p&gt;
&lt;h3 id=&quot;version-2&quot;&gt;Version 2&lt;/h3&gt;
&lt;p&gt;Another side effect of legacy is what is known as “&lt;em&gt;We can no longer develop new features in this codebase. It’s written in Ruby and it’s impossible to find any Ruby developers, so the Architects Council has come up with a decision to rewrite the software to Node.JS. We will call it V2&lt;/em&gt;“.&lt;/p&gt;
&lt;p&gt;And while a rewrite sounds like a good idea from an engineering perspective, it’s a bad idea from a product perspective. There are many nuances you need to think about when doing &lt;em&gt;v2&lt;/em&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Are you going to hire an entirely new team to work on v2, or are you going to repurpose the existing team from v1 to v2?&lt;/li&gt;
&lt;li&gt;What is happening to v1? Are you deprecating it? Are you going to invest development time in it? What happens to customers of v1?&lt;/li&gt;
&lt;li&gt;How are you going to sell v2 to existing customers in v1? Nobody wants to move from a working product to a half-baked prototype with lacking feature parity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;V2s are great for engineers, especially those who are going to lead the development. You get to work with new technologies (usually), greenfield a product, the feature set is usually clear from the beginning (because you are essentially remaking an existing product, but in a maintainable way). In the end, after tears and blood, you get a new, somewhat maintainable, codebase with no legacy. But make no mistake, the life path of V2 will follow V1s - eventually it will become legacy. Because V2s are no more than a bandaid on the big “legacy infection” of your codebase.&lt;/p&gt;
&lt;h2 id=&quot;oh-no-what-can-we-do&quot;&gt;Oh no! What can we do?!&lt;/h2&gt;
&lt;p&gt;Have no fear! Legacy fighter is here.&lt;/p&gt;
&lt;p&gt;The truth is — you can’t avoid legacy. I don’t believe in &lt;em&gt;problem prevention&lt;/em&gt;, especially in software engineering. Problem prevention is equal to &lt;em&gt;magic crystal reading&lt;/em&gt;. But this is a topic for another post. Let’s for now agree that legacy is the byproduct of software development. And the same way as a mess in the kitchen is a byproduct of Shabbat dinner preparations — it needs to be taken care of as soon as possible, before it piles into something you can’t handle.&lt;/p&gt;
&lt;p&gt;This means that you need to have tasks that are &lt;strong&gt;prioritized&lt;/strong&gt; in your sprint (or whatever system you use) in which you can tackle regions of code that are prone to become legacy. You don’t just fill your backlog with “&lt;em&gt;Improve X so that it will do Y&lt;/em&gt;“ tasks and never work on them. You need to dedicate time. Find a balance between features and tech debts. Every sprint should have, let’s say, 80% features development and 20% tech debts. This also needs to be understood by your (a) Engineering Manager and (b) Product Manager. It must be a rule. There should be no exceptions. No “&lt;em&gt;but maybe in this sprint we can focus more on features since we really promised this client that it will happen”&lt;/em&gt;. It’s not your problem, as a software engineer, that your product manager makes promises he can’t fulfill. It’s his job to balance the management of the product. Your job is to balance development of the product. And part of the development is making sure that your code continues to live after you decide to go to a different company; it’s your job to make sure that new developers are not faced with &lt;em&gt;oh no, this is a core functionality, we don’t touch that in our shire&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Apart from &lt;strong&gt;fixing&lt;/strong&gt; legacy, you need to make sure legacy is &lt;strong&gt;identified&lt;/strong&gt;. Legacy does not just appear out of nowhere. Legacy is a good code that was piled on with a bunch of &lt;em&gt;quick fix for client&lt;/em&gt; and &lt;em&gt;a patch to make it work in Explorer&lt;/em&gt; type of development. Every feature and bug fix is a potential to identify legacy. If something is hard to understand, hard to work with — you need to document it. You need to make sure it’s known as a problem to the team, engineering manager and product manager. They need to be aware of the fact that you’ve tried to implement X but encountered a code that you’ve had to debug with other developers, because this code is hard to understand. And once they are aware of it, you prioritize it and start to work on it.&lt;/p&gt;
&lt;p&gt;It’s an endless cycle. And it’s a healthy cycle. It’s part of development. And there will be sprints when you have a lot of features and very few legacy code, and there will be sprints where you have a big refactor to do and hence, you freeze feature development.&lt;/p&gt;
&lt;p&gt;Legacy is not a boogie man. You don’t need to be afraid of it. But if you let it grow, it will grow. And will eventually kill your product and maybe even your company.&lt;/p&gt;</content:encoded></item><item><title>It&apos;s better to be (type)safe than sorry</title><link>https://kudmitry.com/articles/it-is-better-to-be-typesafe-than-sorry/</link><guid isPermaLink="true">https://kudmitry.com/articles/it-is-better-to-be-typesafe-than-sorry/</guid><pubDate>Sat, 19 Feb 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Take a good look at the following function and try to understand what it’s doing.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; do_magic&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is it summing two numbers? Or maybe it’s doing string concatenation? The answer is: you can’t know. And this beats the entire purpose of programming languages – to be easily understandable by humans.&lt;/p&gt;
&lt;h2 id=&quot;a-brief-history-of-dynamically-typed-languages&quot;&gt;A brief history of dynamically typed languages&lt;/h2&gt;
&lt;p&gt;There is a long &lt;a href=&quot;https://en.wikipedia.org/wiki/Dynamic_programming_language#Examples&quot;&gt;list&lt;/a&gt; of dynamically typed languages, but for simplicity, I’ll focus on a few of the most popular.&lt;/p&gt;
&lt;h3 id=&quot;php&quot;&gt;PHP&lt;/h3&gt;
&lt;p&gt;PHP first appeared in 1995, and is officially categorized as a &lt;em&gt;scripting language&lt;/em&gt;. It was created by Rasmus Lerdorf to maintain his personal homepage, which Rasmus wrote as CGI programs in C. It was never intended to be a programming language and Rasmus even said: “&lt;em&gt;I don’t know how to stop it, there was never any intent to write a programming language […] I have absolutely no idea how to write a programming language, I just kept adding the next logical step on the way.&lt;/em&gt;”&lt;/p&gt;
&lt;p&gt;It is now powering 78.1% of all websites, according to W3Techs report from January 2022.&lt;/p&gt;
&lt;h3 id=&quot;python&quot;&gt;Python&lt;/h3&gt;
&lt;p&gt;First appeared in 1991. Its development started 2 years before that, in 1989, by Guido van Rossum. He was working with Amoeba operating system and needed a better language to do system administration, rather than writing C programs.&lt;/p&gt;
&lt;p&gt;Python consistently ranks as one of the most popular programming languages.&lt;/p&gt;
&lt;h3 id=&quot;lua&quot;&gt;Lua&lt;/h3&gt;
&lt;p&gt;Created by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes in 1992, Lua was primarily designed to be embedded in other applications.&lt;/p&gt;
&lt;p&gt;Today, Lua is the leading scripting language for games.&lt;/p&gt;
&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;/h3&gt;
&lt;p&gt;Designed by Brendan Eich and appeared in 1995, JavaScript was created for one sole purpose – add dynamic capabilities to boringly static HTML pages.&lt;/p&gt;
&lt;p&gt;Today, JavaScript is one of the core technologies of the World Wide Web and over 97% of websites use JavaScript.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As you can see, none of these languages were designed to be robust and replace the old C or Pascal. They all managed to find their niches, but they were all languages designed to either be used for personal needs, or with the main purpose of extending other applications which were written in statically compiled languages.&lt;/p&gt;
&lt;p&gt;None of these languages should be used, in their pure state, as the primary languages for application development. Let me elaborate on that.&lt;/p&gt;
&lt;h2 id=&quot;the-human-factor&quot;&gt;The human factor&lt;/h2&gt;
&lt;p&gt;If you’ve ever worked on a serious project, you know that extending and maintaining it – is a challenging task.&lt;/p&gt;
&lt;p&gt;How often you’ve looked at your code, or code you peer reviewed, and you said to yourself, “&lt;em&gt;What a nice, clean solution&lt;/em&gt;”? But after a few months, when you’ve revisited the code, you thought to yourself “&lt;em&gt;How this piece of crap ended up in our code base?&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;When we write code, we are unable to keep the entire context of “&lt;em&gt;why&lt;/em&gt;” that we have in our head at the moment of coding; hence we end up looking at old code and crying. And the fact that we take away types – only makes things worse.&lt;/p&gt;
&lt;h3 id=&quot;one-little-bug&quot;&gt;One little bug&lt;/h3&gt;
&lt;p&gt;I remember hunting a bug in one of the system I was working on. The system had entities that were versioned, and the version was universal across the entire entity domain. Since the number of entities was huge, and with each entity modification, we needed to increase the version, we could easily overflow the 64bit integer. So we’ve decided to use BigInt column in the database to track the version.&lt;/p&gt;
&lt;p&gt;For each create or update operation on an entity, we would call the following function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getCurrentVersion&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;howMany&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; currentVersion&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; magically_get_current_version_from_db&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; nextVersion&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; currentVersion&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; howMany&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; magically_set_current_version_in_db&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;nextVersion&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; currentVersion&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: I’m reconstructing the code from memory&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The entire function was wrapped in a database transaction (to avoid race condition), and the “*magically_**” functions were actually calls to our ORM (please &lt;a href=&quot;https://seldo.com/posts/orm_is_an_antipattern&quot;&gt;don’t use ORMs&lt;/a&gt;) which would fetch the version from the DB, then reserve 1 or more (we could have batch updates) versions, and save it as next.&lt;/p&gt;
&lt;p&gt;What we saw in our development environment was that entities were out of order, and we saw a considerable increase in version numbers. We’ve reached massive numbers very fast.&lt;/p&gt;
&lt;p&gt;Just for an exercise, try to understand what was the problem. Continue reading once you’ve shed some tears.&lt;/p&gt;
&lt;p&gt;It turned out that our ORM returned BigInt as strings. So if our current version in the DB was 123, it would come back from the ORM call as &lt;code&gt;&quot;123&quot;&lt;/code&gt;, and adding an integer (let’s say 1) to it, turned out to be… &lt;em&gt;poopity whoopity&lt;/em&gt;… &lt;code&gt;&quot;1231&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I’ll pause for a minute, so you could enjoy your moment of pseudo-superiority and express your nasty comments about my mediocre programming skills, because we all know that &lt;strong&gt;you&lt;/strong&gt; write perfect code, and compile it in your head before V8’s runtime even finishes interpreting it.&lt;/p&gt;
&lt;p&gt;My point is – it would not have happened in a statically typed language. No compiler in the world would just let you sum a string with an integer, or would at least warn you that you are about to do something stupid.&lt;/p&gt;
&lt;p&gt;And yet, when I proposed people that we need to switch to Typescript, I’ve encountered comments such as: “&lt;em&gt;but I want fast iteration&lt;/em&gt;”, “&lt;em&gt;I had enough of Java headaches&lt;/em&gt;”, “&lt;em&gt;But then I have to define every type&lt;/em&gt;” and my favorite “&lt;em&gt;But we don’t have errors related to dynamic types&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;This bug is only one of many I’ve dealt with in my life. I don’t have enough bits to count how many times I had to print objects just to see their contents. Is it &lt;code&gt;obj.foo_bar&lt;/code&gt; or &lt;code&gt;obj.fooBar&lt;/code&gt;? Not having types is like not having street names. You need to memorize the entire city and knock on each door and ask what street and what number is it. You have to keep the entire project and all of its dependencies in your head (and we all know that &lt;code&gt;node_modules&lt;/code&gt; is like a black hole, so it can’t be kept in your head). Otherwise, you risk forgetting that some library decided not to use native &lt;code&gt;BigInt&lt;/code&gt;, but instead return strings 🤷‍♂️.&lt;/p&gt;
&lt;h2 id=&quot;the-cost-of-bugs&quot;&gt;The cost of bugs&lt;/h2&gt;
&lt;p&gt;With the shift from desktop application towards SaaS software, and because of humans losing patience, developing software became a 24/7 task. You no longer have engineers who write code from 9to5, but you also have 24/7 on-call engineers (which nobody really &lt;a href=&quot;https://news.ycombinator.com/item?id=29368457&quot;&gt;wants to be&lt;/a&gt;) – hence the cost of such bugs is colossal.&lt;/p&gt;
&lt;p&gt;Not only it harms your customers who cannot access your products; it harms your company, which loses money. And it harms you – because Murphy hates you, and it’s your turn to be on-call. So, suddenly, you find yourself half drunk in a bar, on Friday evening, trying to understand why your BigInt is overflowing, while your friends take Vodka shots off your MacBook Pro. But hey, at least you don’t need to define &lt;em&gt;EVERY TYPE&lt;/em&gt;, like a bizarre monster who enjoys writing interfaces in Java.&lt;/p&gt;
&lt;h2 id=&quot;they-arent-made-for-it&quot;&gt;They aren’t made for it&lt;/h2&gt;
&lt;p&gt;None of the dynamically typed languages we are using in our production today are made to be used in such way. You keep hearing about how PayPal or Uber are switching to NodeJS, while forgetting about the fact, that their adoption of NodeJS is for a tiny API Gateway that proxies requests to their huge Java monoliths (probably; I’ve never worked in those companies, but I have 10+ years of software development experience, and know a thing or two about maintaining legacy code).&lt;/p&gt;
&lt;p&gt;You keep embracing dynamic languages, while the world keeps deprecating them. Facebook developed Hack to introduce types into PHP. Even PHP now has support for types. Microsoft came up with TypeScript to ease the pain while writing JavaScript applications; Python 3 has support for type hinting; and surprisingly, but in Lua &lt;code&gt;&quot;123&quot; + 1&lt;/code&gt; produces 124, and &lt;code&gt;&quot;123&quot; + nil&lt;/code&gt; throws an error. Want to guess what’s the output of &lt;code&gt;&quot;123&quot; + null&lt;/code&gt; in JavaScript?&lt;/p&gt;
&lt;h2 id=&quot;its-better-to-be-type-safe-than-sorry&quot;&gt;It’s better to be type safe than sorry&lt;/h2&gt;
&lt;p&gt;A world of fast iterations, race to market and other marketing terms – has no place for dynamically typed languages for business critical applications.&lt;/p&gt;
&lt;p&gt;Don’t get me wrong, dynamic languages are great at what they were designed to do. Python excels at AI and ML applications; Lua is perfect as embeddable scripting language for a type safe application; and JavaScript… it’s still terrible, so please, at least, use TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Nah. I don’t make stupid type related errors&lt;/em&gt; – Well, go to your logs and search for how many &lt;code&gt;cannot read property x of undefined&lt;/code&gt; you have. Chances are you have a few of those logs, and your code is scattered with &lt;code&gt;const bar = foo || {}&lt;/code&gt; - because computers are made to be deterministic, but you are a free spirit and your &lt;code&gt;foo&lt;/code&gt; &lt;em&gt;might&lt;/em&gt; or &lt;em&gt;might not&lt;/em&gt; be an actual object.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I have tests to make sure I never sum integers with strings&lt;/em&gt; – Well, guess what. If you had let compilers do their job, you wouldn’t have to spend developer’s time on writing guard tests for type related problems. Java developers don’t spend time writing tests to make sure &lt;code&gt;int x&lt;/code&gt; is an integer.&lt;/p&gt;
&lt;p&gt;Type safety is not some esoteric obsession of programming geeks in basements. It’s &lt;strong&gt;business critical&lt;/strong&gt; for every company that want to stay in business. And I’d even radically say to you, and ask you to politely tell your manager, that making your code type safe should be treated the same way you treat customer bugs, and not buried in a technical debt pile. Use type hinting, switch to TypeScript or a real statically typed language – I don’t really care, as long as you do it.&lt;/p&gt;
&lt;p&gt;You owe it to yourself and your sanity. And if not yourself, then for the well-being of your company. And if not your company, then, &lt;em&gt;at least&lt;/em&gt;, for the satisfaction of your customers.&lt;/p&gt;</content:encoded></item><item><title>The Software Industry is Broken</title><link>https://kudmitry.com/articles/the-software-industry-is-broken/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-software-industry-is-broken/</guid><pubDate>Wed, 26 Jan 2022 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s that time again. You come to your Toyota dealership to buy a new Toyota Corolla. You’ve been waiting to upgrade your old, fuel inefficient and slow model, with this year’s new model. But the new model is more fuel inefficient! Wait! What?!&lt;/p&gt;
&lt;p&gt;The evolution of products is very clear. Newer should be better. Better equals more fuel efficient, faster, safer. Otherwise, why would you upgrade? Have you ever heard of your friend bragging about his new fridge, telling you how roomy it is, how it has an RGB light inside, but it uses more electricity? 😦&lt;/p&gt;
&lt;p&gt;Or get this. Have you ever heard about a major airline, purchasing a new fleet of Boeing (or Airbus) airplanes, just so they will be less fuel efficient, make your flight longer and the airline needs to rebuild their hub airport because the airplane now can’t taxi to the gate?&lt;/p&gt;
&lt;p&gt;No, you haven’t. Because that’s not what progress is. Progress is a process towards better, not worse. You don’t buy a new car to drive slower or pay more for gas.&lt;/p&gt;
&lt;p&gt;But you know what? In software, that is what we are doing. I’m writing this article, while Spotify is playing in the background, and a quick look at Activity Monitor reveals Spotify has 6 processes running, with an average of 2.2% of CPU usage, and a combined total of 470MB of memory usage! &lt;strong&gt;470MB!!&lt;/strong&gt; Winamp uses around 49MB (with a VM size of 60MB) &lt;a href=&quot;http://forums.shoutcast.com/showthread.php?t=271869&quot;&gt;source&lt;/a&gt;. And Winamp has &lt;strong&gt;visualizations&lt;/strong&gt; ffs!&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/winamp.webp&quot; alt=&quot;Winamp visualizer&quot; height=&quot;1200&quot; width=&quot;675&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Wina-Winamp! It really whips the llama&apos;s ass!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;product-segmentation&quot;&gt;Product Segmentation&lt;/h2&gt;
&lt;p&gt;I’m a developer. I try to have the best machine I can. Max memory, top CPU, best GPU (if I’m working with 3D graphics). We expect it from professionals. The same way professional car drivers try to get the best car; professional mountain bikers own the best bikes; pro photographers get the best camera and lenses.&lt;/p&gt;
&lt;p&gt;But every product has segmentation. Some products are entry level, other aim at professionals. You can get a decent DSLR camera or an entry level mountain bicycle. And you can get an entry level PC or aim at a beefy machine with 64GB RAM and Ryzen 9 5900x (should you need that). But while your entry level DSLR will snap exceptional pictures, and your beginners mountain bicycle will get you through beginner singles, your entry level PC will struggle to keep up with basic software like music players or instant messaging.&lt;/p&gt;
&lt;h2 id=&quot;640k-ought-to-be-enough-for-anybody&quot;&gt;640K ought to be enough for anybody&lt;/h2&gt;
&lt;p&gt;It’s Saturday and I’m on my home PC, so I don’t have Slack open, but a quick Google reveals that slack uses at max around 1GB of RAM. Add to it Spotify, a Chrome with few tabs, the OS itself and you’ve maxed out 4GB of RAM easily.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But Slack is a professional tool!&lt;/em&gt; - no. No, it’s not. It’s a glorified IM with slightly more features that mIRC. And mIRC uses anywhere between 50MB and 250MB (&lt;a href=&quot;https://forums.mirc.com/ubbthreads.php/topics/194177/Memory_usage&quot;&gt;source&lt;/a&gt;). Slack is used in a professional environment, yes, but it does not justify the fact that it eats a good chunk of my memory that I need for my professional work such as an IDE, running compilations and VMs.&lt;/p&gt;
&lt;p&gt;We are in an era when software grows way bigger and heavier, but does not add any features or improvements. Spotify, for example, does not have an equalizer, a feature that Winamp does. Instead, it has friend’s activity, so I can see what are my friends listening to, because I have nothing else to do. Games used to be distributed on single CDs, then multiple DVDs and now you need to download tens of gigabytes over network in order to enjoy the last AAA titles. But with the increase in size and memory usage, we also got an increase in quality and visuals. We went all the way from rectangular faces like this:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/gta3.webp&quot; width=&quot;970&quot; height=&quot;546&quot; alt=&quot;GTA 3&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Busted&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To near realistic graphics like this:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/witcher3.png&quot; width=&quot;650&quot; height=&quot;310&quot; alt=&quot;Witcher 3 - Wild Hunt&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Toss a coin to your Witcher&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;640k ought to be enough for anybody&lt;/em&gt; - is a quote falsely attributed to Bill Gates &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Although he never said that, and obviously staying within the limits of 640K is not progress, the current status quo is not acceptable! We can’t, and shouldn’t, argument everything with “&lt;em&gt;But RAM is cheap&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;Today’s software gets heavier and ships with fewer features. There is a word for this process in the English dictionary: &lt;strong&gt;regress&lt;/strong&gt;. It turns out that it is the antonym of &lt;strong&gt;progress&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;why-it-happens&quot;&gt;Why it happens?&lt;/h2&gt;
&lt;p&gt;Compared to 20-30 years ago, today software is everywhere. It’s even in your &lt;a href=&quot;https://www.samsung.com/us/explore/family-hub-refrigerator/overview/&quot;&gt;fridge&lt;/a&gt;. Think of anything, and high chance it has software in it. And with a significant increase in software availability, a matching demand for software engineers came along.&lt;/p&gt;
&lt;p&gt;And as with any factory production line, optimizations were made. New languages were created. From an old and clunky Fortran, where you write code such as:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;INTEGER(4) :: i,io_unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;REAL(8) :: pmin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;STACK_PUSH&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the way to the new and shiny JavaScript where you write poetry:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;delete&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;/something&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;should&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;have&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;proptery&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;equal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;to&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Bad Request&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As something becomes more mainstream, it takes a simpler form. First cars were hard to drive, today you don’t even have to change the gears. Same with software. Back then you had to understand what are registers and how many bits your integers take. Today, everything is dynamic and the compiler or the runtime knows better than you what types to use.&lt;/p&gt;
&lt;p&gt;While I struggle with this concept sometimes, because I grew up on understanding the difference between little and big endian; I understand the reasoning behind it. When a specialty tool is being adopted by the masses, it will be simplified to suit everyone as much as possible. As long as the adaptation includes improvements, I’m fine with it. This is progress, and the reason we, as humanity, can enjoy technology in the way we enjoy it.&lt;/p&gt;
&lt;p&gt;Creating software today is simpler than ever before, with high level, garbage collected, dynamic languages and &lt;a href=&quot;https://developers.google.com/blockly&quot;&gt;block-like programming&lt;/a&gt;. And the world of software creation opened a lot of business opportunities. You practically can create anything from nothing.&lt;/p&gt;
&lt;h3 id=&quot;a-personal-story&quot;&gt;A personal story&lt;/h3&gt;
&lt;p&gt;Around the age of 16, I had to choose a specialization in school. Once you reach 9th grade in school, you need to decide what specialization you want to get based on your desire and grades. There were specializations like psychology (focusing less on math and more on literature, psychology and philosophy); robotics (with focus on math and programming, and less on literature or psychology); computer science (with higher focus on math and programming, and less on literature and psychology) and others.&lt;/p&gt;
&lt;p&gt;I was torn between robotics and computer science. I was sure it’s going to be the biggest decision of my life (boy, I was wrong). I remember the summer break between 8th and 9th grade, where I was struggling to decide what do I want to do, where do I want to go. I enjoyed creating things by writing code and my major struggle was to decide whether I want to create &lt;em&gt;virtual&lt;/em&gt; things like software, or make software create &lt;em&gt;real&lt;/em&gt; things like robots.&lt;/p&gt;
&lt;p&gt;Programming was already a hobby of mine. And I choose computer science. One of the key factor for my decision was the fact that I come from an immigrant family. We are not poor, but I couldn’t allow myself asking my parents for money to buy resistors, breadboards and soldering station. I’ve already had a computer and knew programming and with that I could create software.&lt;/p&gt;
&lt;h3 id=&quot;software-production-is-relatively-cheap&quot;&gt;Software production is &lt;em&gt;relatively&lt;/em&gt; cheap&lt;/h3&gt;
&lt;p&gt;My “biggest” life decision boiled down to the fact that I had a PC. The upfront cost of software creation is relatively cheap. You don’t need to buy programming languages like you do with heavy machinery, nor you have to rent an office, like you do with a factory or a warehouse.&lt;/p&gt;
&lt;p&gt;And the rewards you can get from software production are massive. We are in a midst of a software revolution, where VC funded startups are being spawned like zombies in Left 4 Dead 2.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/startups.jpg&quot; width=&quot;671&quot; height=&quot;372&quot; alt=&quot;Startups are everywhere&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Look! Startups are everywhere&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;nobody-cares&quot;&gt;Nobody cares&lt;/h3&gt;
&lt;p&gt;Software surrounds us, and yet, in my opinion, it’s one of the inefficient industries we have. The solution to slow software is to buy more RAM or spin up yet another AWS instance. Ain’t nobody got time to figure out what’s wrong. Optimization became a hated word used by heretics. All our optimization problems are solved by increasing the instance type or instances count. In the end, everybody suffers and nobody really cares.&lt;/p&gt;
&lt;p&gt;Users suffer. But they don’t care. They have nothing to do. Their choice is mostly between either using the product or not, or between two different software when neither of them is optimized enough or lack features.&lt;/p&gt;
&lt;p&gt;Developers don’t care because it’s too hard to write proper software. It’s too hard to do proper error handling or debugging. They prefer to spend their time in creating &lt;a href=&quot;https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition&quot;&gt;FizzBuzz&lt;/a&gt; architectures and appearing smart in front of their colleagues during design reviews. And their simple To-Do app is grown to a pile of shitty abstractions that nobody can understand how to debug and optimize.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/ops.webp&quot; width=&quot;531&quot; height=&quot;359&quot; alt=&quot;Oops, something went wrong&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Imagine your car asking you to pull aside, turn off and then turn on because &apos;something went wrong&apos;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Business and VCs obviously don’t care as well. They care only about 3-letter-abbreviates such as TTM and ROI. The lower the first and higher the second - the happier they are. Tech debts are being buried beneath piles of optimization bugs that are fixed by occasionally restarting the servers. At least we do a retrospective!&lt;/p&gt;
&lt;p&gt;When I type &lt;code&gt;google.com&lt;/code&gt; in my browser, my computer issues a network request that, according to &lt;code&gt;traceroute&lt;/code&gt; tool, travels between 20 different network hops and takes about 75ms to reach Google servers. &lt;strong&gt;75ms&lt;/strong&gt;. Let that number sink for a moment. It travels between 20 different routers, most of them are with shitty (compared to your phone, yeah?) 1GHz CPUs and 1GB of RAM, serving millions of requests, via fiber or copper wiring, to a &lt;strong&gt;different continent&lt;/strong&gt;! LET THAT SINK! And yet, it takes few &lt;strong&gt;seconds&lt;/strong&gt; from the moment I open slack till the moment it’s useable, and I have fully spec-ed Mac Mini. 🤯&lt;/p&gt;
&lt;h2 id=&quot;the-future&quot;&gt;The future&lt;/h2&gt;
&lt;p&gt;We had 4KB or RAM when we landed on the moon&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, but today it takes &lt;strong&gt;100000&lt;/strong&gt; more to send your colleague a message. Yes, RAM is cheap. Yes, machines are powerful. But progress is about doing the best you can. And I’m pretty sure that 400MB IM is not the best we can.&lt;/p&gt;
&lt;p&gt;There is beautiful software out there. Efficient, snappy, feature-rich. There are engineers who care about performance, who care about getting the maximum out of the available technology. Not wasting resources. But they are in the minority.&lt;/p&gt;
&lt;p&gt;Imagine a world where you can open a software and it’s useable right away. You don’t need to wait for it to load the redundant friend’s activity. It doesn’t have useless features. And it works on your entry level PC. Such a world is possible, and we are responsible for making it.&lt;/p&gt;
&lt;p&gt;I encourage everybody to take an action. Refuse to write inefficient software. Demand time for Tech Debts that focus on optimization. Be open-minded and choose the best tool for the problem. Together we can fix the software industry.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://quoteinvestigator.com/2011/09/08/640k-enough/&quot;&gt;Quote Investigator&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.metroweekly.com/2014/07/to-the-moon-and-back-on-4kb-of-memory/&quot;&gt;MetroWeekly - To the Moon and back on 4KB of Memory&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>NodeJS Native Module vs WASM</title><link>https://kudmitry.com/articles/native-rust-wasm/</link><guid isPermaLink="true">https://kudmitry.com/articles/native-rust-wasm/</guid><pubDate>Sat, 11 Dec 2021 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my previous post about &lt;a class=&quot;internal new&quot; href=&quot;/articles/supercharge-nodejs-with-rust/&quot;&gt;Native Rust Modules for NodeJS&lt;/a&gt;, people asked me how neon bindings would compare to WASM. Let’s check!&lt;/p&gt;
&lt;h2 id=&quot;what-is-wasm&quot;&gt;What is WASM?&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;WebAssembly&lt;/strong&gt; (sometimes abbreviated &lt;strong&gt;Wasm&lt;/strong&gt;) is an &lt;a href=&quot;https://en.wikipedia.org/wiki/Open_standard&quot;&gt;open standard&lt;/a&gt; that defines a &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_portability&quot;&gt;portable&lt;/a&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Binary_code&quot;&gt;binary-code&lt;/a&gt; format for &lt;a href=&quot;https://en.wikipedia.org/wiki/Executable&quot;&gt;executable programs&lt;/a&gt;, and a corresponding &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format&quot;&gt;text format&lt;/a&gt;, as well as interfaces for facilitating interactions between such programs and their host environment.The main goal of WebAssembly is to enable high-performance applications on &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_page&quot;&gt;web pages&lt;/a&gt;, but the format is designed to be executed and integrated in other environments as well, including standalone ones.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/WebAssembly&quot;&gt;Wikipedia&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;WASM (or WebAssembly) is a binary code. It is executed by a portable VM that is currently implemented in &lt;a href=&quot;https://caniuse.com/?search=wasm&quot;&gt;all major browsers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, as Assembly, WASM is not that pleasant to write in and therefore other languages, and Rust among them, support WASM as compilation target.&lt;/p&gt;
&lt;p&gt;The purpose of this article is to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do a quick introduction to compiling Rust into WASM&lt;/li&gt;
&lt;li&gt;Benchmark Pure JS, Native Module and WASM Module implementations of the same function&lt;/li&gt;
&lt;li&gt;Provide basic guidelines towards when to choose either implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Please make sure you’ve read my &lt;a class=&quot;internal new&quot; href=&quot;/articles/supercharge-nodejs-with-rust/&quot;&gt;previous article&lt;/a&gt;, because this one is built on it. You can find the full code for this tutorial on &lt;a href=&quot;https://github.com/skwee357/rust-native-vs-wasm&quot;&gt;GitHub&lt;/a&gt;. Let’s dive in.&lt;/p&gt;
&lt;h2 id=&quot;how-to-compile-rust-to-wasm&quot;&gt;How to compile Rust to WASM&lt;/h2&gt;
&lt;p&gt;For native modules we have Neon, for WASM we have &lt;a href=&quot;https://github.com/rustwasm/wasm-bindgen&quot;&gt;wasm-bindgen&lt;/a&gt;. &lt;code&gt;wasm-bindgen&lt;/code&gt; gives the tools needed for importing JS functions into Rust and exporting Rust function to JS. Let us look at how we can create a WASM version of our &lt;code&gt;fibonacci&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;First, we need to make sure our &lt;code&gt;Cargo.toml&lt;/code&gt; lists &lt;code&gt;wasm-bindgen&lt;/code&gt; as dependency like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;wasm-bindgen&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;0.2.78&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, everything that is left, is to mark our function with a special macro, like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; wasm_bindgen&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;prelude&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::*;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;#[wasm_bindgen]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        _&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) + &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it! 🎉&lt;/p&gt;
&lt;p&gt;Thank you for reading. See you in the next one.&lt;/p&gt;
&lt;p&gt;Not really. I suggest you read the &lt;a href=&quot;https://rustwasm.github.io/docs/wasm-bindgen/introduction.html&quot;&gt;wasm-bindgen Guide&lt;/a&gt;, there is a lot of information about how to interact with JS from Rust and vice versa and information regarding what types are supported and how you can transfer them between the two languages.&lt;/p&gt;
&lt;p&gt;In order to build the WASM, we need another tool, called &lt;a href=&quot;https://github.com/rustwasm/wasm-pack&quot;&gt;wasm-pack&lt;/a&gt;, We then can execute the following command &lt;code&gt;wasm-pack build --target nodejs&lt;/code&gt;, which will produce a NodeJS compatible WASM (you can read more about different targets &lt;a href=&quot;https://rustwasm.github.io/wasm-pack/book/commands/build.html#target&quot;&gt;here&lt;/a&gt;). The resulting code will be placed in &lt;code&gt;pkg&lt;/code&gt; directory and will contain 4 files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;index.d.ts&lt;/code&gt; – Typescript definitions for our module&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt; – The main file that will initialize the &lt;code&gt;WebAssembly&lt;/code&gt; module and create a &lt;code&gt;WebAssembly&lt;/code&gt; instance that we can interact with (feel free to take a look inside, its very readable)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.wasm&lt;/code&gt; – The actual machine code of the WebAssembly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.wasm.d.ts&lt;/code&gt; – Typescript definitions for the WASM file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You then simply require the files from &lt;code&gt;pkg&lt;/code&gt; dir and use them as regular JavaScript (or TypeScript).&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;fibonacci_wasm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;./pkg/index&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci_wasm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;what-about-performance&quot;&gt;What about Performance?&lt;/h2&gt;
&lt;p&gt;Ah. Performance. The thing that Javascript developers care for the most.&lt;/p&gt;
&lt;p&gt;I’m going to use the &lt;a href=&quot;https://github.com/sharkdp/hyperfine&quot;&gt;hyperfine&lt;/a&gt; tool, run each &lt;code&gt;fibonacci&lt;/code&gt; with 3 warmup runs, computing different numbers, taking the mean running time, and present you the results. Please note: &lt;strong&gt;These are not laboratory grade benchmarks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;All benchmarks are run on 2020 Mac Mini M1 with 16GB of memory.&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Runtime&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;30th Fibonacci&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;44th Fibonacci&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;45th Fibonacci&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;46th Fibonacci&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;JavaScript (NodeJS)&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;165.2ms&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;5.846s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;9.358s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;15.038s&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Native Rust&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;161.5ms&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2.271s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;3.578s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;5.721s&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Rust WASM&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;163ms&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;3.286s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;5.207s&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;8.317s&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;analysis&quot;&gt;Analysis&lt;/h3&gt;
&lt;p&gt;I hope no body is surprised that Rust took the 1st place, followed by WASM, while JavaScript was finishing last.&lt;/p&gt;
&lt;p&gt;It’s interesting though to see that on low numbers, such as 30th Fibonacci number, all 3 methods performed roughly the same, with Rust being 2.23% faster and WASM being 1.33% faster. This proves again that you always need to benchmark a specific function / method before assuming that a switch to low-level languages will perform better.&lt;/p&gt;
&lt;p&gt;However, once we go higher in the Fibonacci numbers, we can see a clear difference between the 3 methods.&lt;/p&gt;
&lt;p&gt;We can see that for JavaScript, the jump between 44th and 45th Fibonacci numbers resulted in an increase of &lt;strong&gt;60.07%&lt;/strong&gt; in time, while the jump from 45th to 46th number resulted in &lt;strong&gt;60.69%&lt;/strong&gt; increase.&lt;/p&gt;
&lt;p&gt;For Native Rust module, the increase resulted in &lt;strong&gt;57.55%&lt;/strong&gt; and &lt;strong&gt;59.89%&lt;/strong&gt; respectively, while for WASM the numbers were &lt;strong&gt;58.46%&lt;/strong&gt; and &lt;strong&gt;59.72%&lt;/strong&gt; respectively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion 1:&lt;/strong&gt; Rust was more efficient in computing the next number in the chain followed by WASM while JS taking bronze medal in this race.&lt;/p&gt;
&lt;p&gt;Let’s continue. Native Rust was &lt;strong&gt;61.15%&lt;/strong&gt; faster than JS in computing the 44th number, while WASM was &lt;strong&gt;43.79%&lt;/strong&gt; faster. For 45th and 46th numbers, Rust was &lt;strong&gt;61.76%&lt;/strong&gt; and &lt;strong&gt;61.95%&lt;/strong&gt; faster respectively, while WASM was &lt;strong&gt;44.35%&lt;/strong&gt; and &lt;strong&gt;44.69%&lt;/strong&gt; faster, respectively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion 2:&lt;/strong&gt; Opting for Native Rust module, increased the performance by &lt;strong&gt;60%&lt;/strong&gt; on average! Opting for WASM, increased the performance by almost &lt;strong&gt;45%&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Rust was &lt;strong&gt;44.69%&lt;/strong&gt; faster than WASM in computing the 44th number; &lt;strong&gt;45.52%&lt;/strong&gt; faster in computing the 45th number, and &lt;strong&gt;45.37%&lt;/strong&gt; faster in computing the 46th number.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion 3:&lt;/strong&gt; Rust on average was &lt;strong&gt;45%&lt;/strong&gt; faster than WASM.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hate benchmarks. Especially on useless functions like Fibonacci. You should always do side-by-side comparison for &lt;strong&gt;your use case&lt;/strong&gt;, rather than rely on benchmarks like this one. However, this benchmark can give us some base line assumptions. We can see that JavaScript is fast enough on lower Fibonacci numbers, however, it struggles as we go higher.&lt;/p&gt;
&lt;p&gt;It’s no surprise that WebAssembly is taking a strong second place. One of its goals was to provide &lt;em&gt;near-native code execution speed&lt;/em&gt;. However, considering the fact that WebAssembly is executed by a VM, it will be hard to achieve real native performance, like with Rust.&lt;/p&gt;
&lt;h2 id=&quot;what-you-should-choose-and-when&quot;&gt;What you should choose and when?&lt;/h2&gt;
&lt;p&gt;I won’t discuss when and why you should choose JavaScript. Chances are, if you are reading this article, that you’ve already chosen JavaScript but looking at how to improve performance in critical parts of your application. Therefore, I’ll focus on providing the differences between Native modules in Rust and WebAssembly.&lt;/p&gt;
&lt;h3 id=&quot;native-or-webassembly&quot;&gt;Native or WebAssembly?&lt;/h3&gt;
&lt;p&gt;First, you need to understand the major difference between the two. Native modules are modules that are written in a compiled language like Rust or C/C++ and are imported via &lt;code&gt;node-ffi&lt;/code&gt; to use in NodeJS applications. They are no different from &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt; files that are loaded by, say, Java JNI or any other language with FFI support.&lt;/p&gt;
&lt;p&gt;WebAssembly on the other hand is a &lt;strong&gt;compilation target&lt;/strong&gt;. What it means is that the best way to write WASM is to actually compile another language, such as Rust or C/C++ &lt;strong&gt;into&lt;/strong&gt; WebAssembly. Yes, you can write WASM directly, by writing a WAT (WebAssembly Text) files and then translating them to WASM by using &lt;a href=&quot;https://github.com/webassembly/wabt&quot;&gt;wabt&lt;/a&gt;, but why would you do that? I won’t argue that x86 Assembly is a useful language to know for certain applications (for example extreme optimization cases for Games or Digital Audio Workstations), WASM, on the other hand, as a language is useless, because many higher-level languages can be compiled to WASM. And since WASM is mainly a compilation target, I &lt;em&gt;assume&lt;/em&gt; the difference in performance between a C++ or Rust implementation compiled to WASM will boil down to the actual performance of the code written in C++/Rust, and how good are the tools that compile said code to WASM (&lt;a href=&quot;https://en.wikipedia.org/wiki/Emscripten&quot;&gt;Emscripten&lt;/a&gt; in that case). Other than that, there should be no performance difference whether the WASM was compiled from C or from Rust.&lt;/p&gt;
&lt;p&gt;Having said that, there is one big difference between the two. When I’ve started to do the research for this article, I wanted to focus on a more realistic problem rather than Fibonacci. I’ve downloaded at 14.7MB CSV file that contains ranges of IPv4 addresses that are mapped to different countries. My idea was to parse this CSV using the 3 methods, load the data into memory as an array, and then perform a lookup of 50 random IPs to find out to which countries they belong to. This would simulate a scenario where you have huge chunk of data being loaded into memory and you need to scan it to find a specific value.&lt;/p&gt;
&lt;p&gt;I’ve successfully completed the task for JavaScript and for Native Rust module, but when I’ve finished writing it for WASM, upon opening the CSV file from the Disk - I’ve got a &lt;code&gt;panic&lt;/code&gt; call. This was the first time I’ve realized the biggest difference between Native Rust module and WASM.&lt;/p&gt;
&lt;h4 id=&quot;a-short-trip-to-the-land-called-std---the-standard-library&quot;&gt;A short trip to the land called &lt;code&gt;std&lt;/code&gt; - The standard library&lt;/h4&gt;
&lt;p&gt;You see, compiled languages can be compiled for different operating systems and target architectures. The two biggest architectures as of today are &lt;code&gt;x86&lt;/code&gt; / &lt;code&gt;x86_64&lt;/code&gt; (The standard 32bit and 64bit architecture we know, that are implemented by Intel and AMD) and &lt;code&gt;armv7&lt;/code&gt; thanks to Smartphones, Raspberry PI and the recent Mac M1 processors. Each architecture handles things like floats and math differently, therefore &lt;code&gt;gcc&lt;/code&gt; (the C compiler) and &lt;code&gt;rustc&lt;/code&gt; (the Rust compiler) need to know how to produce the machine code suited for each architecture. On top of all that, each operating system such as Linux, MacOS and Windows, has it’s own way to manage things like file descriptors and network operations. When you write &lt;code&gt;File::open&lt;/code&gt; in Rust, what it does is actually calling the OS defined method to handle file opening. It fact, the &lt;code&gt;File&lt;/code&gt; struct in Rust is no more than simply&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;pub&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    inner&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;fs_imp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While &lt;code&gt;fs_imp::File&lt;/code&gt; being the actual filesystem implementation (be it NTFS or ext4) which is handled by the OS . &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;std&lt;/code&gt; is very interesting, I even had some small experience implementing my own &lt;code&gt;memset&lt;/code&gt;, around 10 years ago when I was toying with my own &lt;a href=&quot;https://github.com/skwee357/wrap-os/blob/master/kernel/src/klib/memory.cpp&quot;&gt;OS kernel&lt;/a&gt;. But what’s the connection to WASM?&lt;/p&gt;
&lt;p&gt;Well, remember I’ve said that WASM is a &lt;strong&gt;compilation target&lt;/strong&gt;? So essentially, when you are running &lt;code&gt;wasm-pack&lt;/code&gt; somewhere down the line it calls &lt;code&gt;cargo build&lt;/code&gt; (which in turns calls &lt;code&gt;rustc&lt;/code&gt;) and it passes &lt;code&gt;cargo&lt;/code&gt; a special argument, &lt;code&gt;--target&lt;/code&gt; that is equal to &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt;. It mens that we are compiling a &lt;code&gt;wasm&lt;/code&gt; 32 bit code on an unknown vendor with unknown system (other examples of target might include things like &lt;code&gt;x86_64-pc-windows-msvc&lt;/code&gt; meaning that we are compiling to an Intel/AMD 64bit architecture, on PC, for Windows using the MSVC &lt;a href=&quot;https://en.wikipedia.org/wiki/Application_binary_interface&quot;&gt;ABI&lt;/a&gt;. You can list all supported targets with &lt;code&gt;rustc --print target-list&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;And since WASM is a compilation target with unknown vendor and system, there is no &lt;code&gt;stdlib&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;In fact, we can go to the &lt;code&gt;unsupported&lt;/code&gt; system in Rust source code, and see that &lt;code&gt;File::open&lt;/code&gt; actually calls to &lt;code&gt;unsupported()&lt;/code&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; function, which simply panics.&lt;/p&gt;
&lt;p&gt;And this leads me to the biggest difference between the two — if you need to use any of the &lt;code&gt;stdlib&lt;/code&gt; utils such as accessing the filesystem, accessing network, threads and anything related to the OS - &lt;strong&gt;chose native Modules&lt;/strong&gt;. WASM simply can’t support this functionality, since it was designed to be executed by a VM that is running in a sandboxed environment.&lt;/p&gt;
&lt;h5 id=&quot;correction&quot;&gt;Correction&lt;/h5&gt;
&lt;p&gt;&lt;em&gt;Upon reading more, I’ve realized that there are 2 compilation targets for WASM: &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; which we’ve already seen and &lt;code&gt;wasm32-wasi&lt;/code&gt;. &lt;a href=&quot;https://wasi.dev/&quot;&gt;WASI&lt;/a&gt; or Web Assembly System Interface is, a still in development, standard to get safe access to some resources of the OS such as &lt;code&gt;fd_read&lt;/code&gt;, &lt;code&gt;fd_write&lt;/code&gt; and etc. Some WASM VMs provide support for WASI, but for my understanding, no browser currently supports WASI as it’s not fully standardized. Back to the article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Below is a simple guideline when to choose Native vs WebAssembly (as I don’t have vast experience with both methods in order to give you a definitive flowchart).&lt;/p&gt;
&lt;h3 id=&quot;comparison-between-native-modules-and-webassembly&quot;&gt;Comparison between Native Modules and WebAssembly&lt;/h3&gt;
&lt;h4 id=&quot;performance&quot;&gt;Performance&lt;/h4&gt;
&lt;p&gt;Due to the fact that WASM is executed by a VM, native modules will, &lt;em&gt;most likely&lt;/em&gt;, be more performant that their WASM counterpart.&lt;/p&gt;
&lt;h4 id=&quot;reusability&quot;&gt;Reusability&lt;/h4&gt;
&lt;p&gt;Native modules are also reusable. We can use them in any other language that supports FFI. So, for example, if you have a shared logic in Rust native module, you can load it from Nodejs and from Python (using &lt;a href=&quot;https://cffi.readthedocs.io/en/latest/&quot;&gt;CFFI&lt;/a&gt;) or Ruby (using &lt;a href=&quot;https://github.com/ffi/ffi&quot;&gt;Ruby FFI&lt;/a&gt;). WebAssembly on the other hand, can only be run by WASM VM.&lt;/p&gt;
&lt;h4 id=&quot;ergonomics&quot;&gt;Ergonomics&lt;/h4&gt;
&lt;p&gt;In my opinion, the ergonomics of &lt;code&gt;wasm_bindgen&lt;/code&gt; are way better than &lt;code&gt;neon&lt;/code&gt;. I like &lt;code&gt;neon&lt;/code&gt;, don’t get me wrong, but all you need to do in order to export a function from Rust to WASM is to add &lt;code&gt;#[wasm_bindgen]&lt;/code&gt; before the function. With &lt;code&gt;neon&lt;/code&gt; you need to mess with conversions.&lt;/p&gt;
&lt;h4 id=&quot;portability&quot;&gt;Portability&lt;/h4&gt;
&lt;p&gt;Native modules depend on the host machine. WASM being run by a VM is very portable format — get a &lt;code&gt;.wasm&lt;/code&gt; file and it’s guaranteed to run in any environment that has a WASM VM. Native modules need to be recompiled for each host. It’s not that big of a deal if you are using lean docker containers such as &lt;code&gt;alpine&lt;/code&gt; but it is something you need to be aware of, and the &lt;em&gt;“I don’t know, it works on my machine”&lt;/em&gt; becomes a real issue.&lt;/p&gt;
&lt;h4 id=&quot;entry-barrier-for-new-developers&quot;&gt;Entry barrier for new developers&lt;/h4&gt;
&lt;p&gt;Native modules can be written, &lt;em&gt;mainly&lt;/em&gt;, in two languages: C/C++ or Rust. C/C++ get a lot of hate, while Rust is the most loved language &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. But Rust is not simple. In C/C++ you fight with &lt;code&gt;core dumps&lt;/code&gt;, in Rust you fight the &lt;code&gt;borrow checker&lt;/code&gt;. It’s not the most simple language to grasp for someone who is not familiar with the concepts of memory management and pointers.&lt;/p&gt;
&lt;p&gt;WASM being a compilation target, can be produced from many languages. The main ones are C/C++ and Rust, but there is also partial support for Python, Java, Ruby and Go. And once WASM will support multi-threading and garbage collection, C# will be a candidate as well. More over, there is a special language, with TypeScript like syntax, called &lt;a href=&quot;https://en.wikipedia.org/wiki/AssemblyScript&quot;&gt;AssemblyScript&lt;/a&gt;, that was created with one purpose — to be compiled to WASM.&lt;/p&gt;
&lt;p&gt;So the entry barrier to WASM is simpler, in my opinion.&lt;/p&gt;
&lt;h4 id=&quot;node-vs-browser&quot;&gt;Node vs Browser&lt;/h4&gt;
&lt;p&gt;While both this post and my &lt;a class=&quot;internal new&quot; href=&quot;/articles/supercharge-nodejs-with-rust/&quot;&gt;previous one&lt;/a&gt; focuses mainly on NodeJS, we can’t forget the browser. JavaScript is still, and probably forever will be, king in the Browser. And Native modules can’t be executed in the browser. Or any other place that is not NodeJS runtime. JavaScript does not (yet?) support FFI.&lt;/p&gt;
&lt;p&gt;So in case you are developing for the browser, or any other system that has access to JavaScript runtime and WASM VM – your only choice is WebAssembly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Native modules and WebAssembly are both performance optimizations to JavaScript, but they are here to solve different problems. Native modules are here to &lt;strong&gt;extend&lt;/strong&gt; NodeJS with performant code while giving you full access to &lt;code&gt;stdlib&lt;/code&gt;. WebAssembly is here to &lt;strong&gt;replace&lt;/strong&gt; non-performant JavaScript code with a near-native performance binary that is executed in a sandboxed environment.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/rust/blob/master/library/std/src/fs.rs#L92&quot;&gt;Rust fs.rs&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/rust/blob/master/library/std/src/sys/unsupported/fs.rs#L178&quot;&gt;Rust fs.rs&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://insights.stackoverflow.com/survey/2021#technology-most-loved-dreaded-and-wanted&quot;&gt;Stackoverflow 2021 survey&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>JetBrains Fleet Preview - First Impression</title><link>https://kudmitry.com/articles/jetbrains-fleet-preview/</link><guid isPermaLink="true">https://kudmitry.com/articles/jetbrains-fleet-preview/</guid><pubDate>Fri, 10 Dec 2021 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week JetBrains launched their new Fleet IDE&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and I’ve been lucky enough to get the Preview version. What do I think about it?&lt;/p&gt;
&lt;p&gt;I must admit that I adore JetBrains tools. I’m using them since the days they had ReSharper and PHPStorm. It is the single most productive piece of software I own for programming. I’ve used almost all of their tools including IntelliJ IDEA, PHPStorm, WebStrom, CLion (with Rust plugin), GoLang, PyCharm, Resharper (which is now more or less obsolete since they have Rider), and DataGrip.&lt;/p&gt;
&lt;p&gt;Of course, when I’ve read the news that they released a new IDE called Fleet, I had to get it.&lt;/p&gt;
&lt;h2 id=&quot;fleet-at-a-high-level&quot;&gt;Fleet at a high level&lt;/h2&gt;
&lt;p&gt;Fleet is a very lightweight IDE. I never complained about JetBrains tools, I know some people consider their tools to be heavy, I never had any problems with them, but Fleet feels more like a text editor rather than an IDE, in terms of speed.&lt;/p&gt;
&lt;p&gt;Fleet is also very simple. I don’t know if this is intentionally or just limitations of the Preview version. In terms of architecture, Fleet is a total rewrite and it builds upon great tools such as Rust, Kotlin, and Skija + AWT &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; as well as on 20 years of JetBrains expertise in writing IDEs. Fleet has a distributed architecture which you can read more about &lt;a href=&quot;https://www.jetbrains.com/help/fleet/1.0/architecture-overview.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fleet is polyglot meaning it supports the following languages and technologies: Java, Kotlin, Python, Go, &lt;a href=&quot;/tag/javascript&quot;&gt;JavaScript&lt;/a&gt;, &lt;a href=&quot;/tag/rust&quot;&gt;Rust&lt;/a&gt;, TypeScript, and JSON. According to JetBrains announcement, support for PHP, C++, C# and HTML is &lt;em&gt;coming soon&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And this marks a step in the right direction. Development today is a lot more than just Java on the backend and JavaScript on the frontend. Developers are writing API proxies in NodeJS, microservices in Go, and business logic in Java. The need to constantly switch between different IDEs is not that comfortable, and with Fleet, you can have your entire codebase in one IDE.&lt;/p&gt;
&lt;p&gt;Let’s take a deeper look at some of the features of Fleet.&lt;/p&gt;
&lt;h3 id=&quot;the-ui&quot;&gt;The UI&lt;/h3&gt;
&lt;p&gt;Fleet has 2 main components: The Editor and Panels.&lt;/p&gt;
&lt;p&gt;The editor is, well, for editing your code. Apart from the editor, you can have up to 3 panels: left, right and bottom. Each panel can be configured to show multiple tabs that can show one of the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Files Tree - the files tree of your project&lt;/li&gt;
&lt;li&gt;Debug - debug output when debugging the application&lt;/li&gt;
&lt;li&gt;Git - the Git changes with an option to commit them&lt;/li&gt;
&lt;li&gt;Git history - the history of the git repository&lt;/li&gt;
&lt;li&gt;Search - search results&lt;/li&gt;
&lt;li&gt;Terminal - the terminal&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/fleet-ui.png&quot; width=&quot;1920&quot; height=&quot;1200&quot; alt=&quot;Fleet UI&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Fleet UI with 2 panels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The UI is also context-aware and can show you information such as how many implementations your Rust struct has.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/images/context-aware.png&quot; width=&quot;501&quot; height=&quot;211&quot; alt=&quot;UI Context&quot; class=&quot;object-fill m-auto&quot;&gt;
&lt;figcaption&gt;Fleet showing &quot;Code Vision&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There is also an overview window in which you can switch between files, perform actions, jump between different tools and perform a search. It reminds the &lt;em&gt;catch-all&lt;/em&gt; window you’d use by double-tapping the Shift key in their other IDEs.&lt;/p&gt;
&lt;p&gt;Overall the UI is very minimal and clean which again makes you feel like you are using a sophisticated text editor rather than an IDE.&lt;/p&gt;
&lt;p&gt;Few things I miss from the original JetBrains IDEs, and I hope that those are just limitations of the Preview version:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I’d really like to see &lt;code&gt;Dracula&lt;/code&gt; theme. I believe this is the best IDE theme that was ever created, and the default dark theme of Fleet is not appealing.&lt;/li&gt;
&lt;li&gt;In the files tree, I’d like to have a different color for Git ignored paths. Original JetBrains IDEs tend to color ignored folders as well as mark library roots (such as &lt;code&gt;node_modules&lt;/code&gt;). This makes project navigation way easier.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;collaboration&quot;&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;The years 2020 and 2021 marked a new era in the software engineering industry - the era of remote collaboration. Fleet is built with collaboration in mind. It allows multiple people to work simultaneously on the same files, rut test, and more.&lt;/p&gt;
&lt;p&gt;I never worked on collaborative IDEs, but I believe it’s a nice addition for distributed teams or as a remote pair-programming.&lt;/p&gt;
&lt;h3 id=&quot;focused-more-on-hacking-than-ui&quot;&gt;Focused more on hacking than UI&lt;/h3&gt;
&lt;p&gt;I’m not sure if it’s intentional or just the limitations of MVP, but in Fleet, you don’t have fancy configuration windows. Want to enable or disable an IDE setting? Create a &lt;code&gt;.fleet/settings.json&lt;/code&gt; file and use this &lt;a href=&quot;https://www.jetbrains.com/help/fleet/1.0/settings.html&quot;&gt;reference&lt;/a&gt; to configure Fleet.&lt;/p&gt;
&lt;p&gt;What about &lt;em&gt;Run Configuration&lt;/em&gt;? Create &lt;code&gt;.fleet/run.json&lt;/code&gt; and consult this &lt;a href=&quot;https://www.jetbrains.com/help/fleet/1.0/run-configurations.html&quot;&gt;reference&lt;/a&gt; for settings.&lt;/p&gt;
&lt;p&gt;A great usage for this is actually checking in your &lt;code&gt;run.json&lt;/code&gt; to git so that everyone on the team can have the same run configurations for the project. I know you can &lt;em&gt;technically&lt;/em&gt; do the same with their other IDEs, but I have never seen anyone commit their &lt;code&gt;.idea&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Some people might like this approach, others might not. I, personally, feel comfortable with configuring my IDE via JSON files.&lt;/p&gt;
&lt;h3 id=&quot;what-about-vs-code&quot;&gt;What about VS Code?&lt;/h3&gt;
&lt;p&gt;A Fleet review without mentioning VS Code? 😱&lt;/p&gt;
&lt;p&gt;Well, the truth is… I don’t like VS Code. I never understood the hype around it. When I tried it, it was slow, it was clunky.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;BUT TEH PLUGINZZZZ!!!!111&lt;/em&gt;. Yes, looks like the selling point of VS Code. Plugins. I personally don’t see it as a selling point. I want to open my IDE and code, not mess with plugins and incompatibilities. (Yes, I use plugin in JetBrains IDEs. But those plugin are mainly language additions like Rust or TOML support, and not &lt;em&gt;make-your-text-editor-ide-like&lt;/em&gt; sort of plugins).&lt;/p&gt;
&lt;p&gt;So sorry folks, no VS Code comparison today.&lt;/p&gt;
&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;
&lt;p&gt;I’m excited about Fleet. It’s a refreshing IDE that suits the new realities of the world. It’s built on Skia, a C++ graphics library as opposed to Electron. It’s fast. It’s snappy and it’s minimal.&lt;/p&gt;
&lt;p&gt;I won’t, however, ditch my other JetBrains IDEs, yet. Fleet is still missing some things I use daily such as auto-import in Rust; support for HTML/CSS (which should come soon); reopening previous files after restarting Fleet; confirmation windows for deleting files. I’m sure Fleet will improve. JetBrains has the tools and knowledge to do so.&lt;/p&gt;
&lt;p&gt;For me, Fleet takes a great place as an IDE instead of Sublime. It’s perfect for things like messing with my blog (which is powered by &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;), or doing a quick code for an article (new post coming soon, &lt;a href=&quot;https://yieldnews.substack.com&quot;&gt;subscribe to my newsletter&lt;/a&gt; to get updates).&lt;/p&gt;
&lt;p&gt;JetBrains did not say anything about pricing as well. As someone who pays for the &lt;em&gt;All Products Pack&lt;/em&gt;, it will be great to see Fleet being offered as part of it, otherwise users like myself will have a hard time justifying paying for another IDE while having access to all the other JetBrains tools. On the same note, it looks like Fleet is entering the ground of VS Code as its main rival. And since one of the main selling points of VS Code for developers, is that it is free, Fleet will have a hard time competing with VS Code if it will be paid.&lt;/p&gt;
&lt;p&gt;I’ll keep following Fleet. I hope it will position itself correctly in the toolbox of IDEs.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;[Fleet] &lt;a href=&quot;https://www.jetbrains.com/fleet/&quot;&gt;1&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;[Twitter] &lt;a href=&quot;https://twitter.com/en_Dal/status/1465248632949592073&quot;&gt;2&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>How to be a Great Technical Interviewer</title><link>https://kudmitry.com/articles/how-to-be-a-great-technical-interviewer/</link><guid isPermaLink="true">https://kudmitry.com/articles/how-to-be-a-great-technical-interviewer/</guid><pubDate>Sat, 27 Nov 2021 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my career, I’ve interviewed close to 150 people by phone, on-site, and recently — via zoom. I’ve criticized the existing on-site interview and took ownership to reshape it. I’ve inspired others to reshape the phone interview. I am mentoring and onboarding new interviewers to the pool. And I’ve realized that the technical interview is not perfect. Before we jump to explore the problematic points, let’s first learn how we came to it in the first place.&lt;/p&gt;
&lt;h2 id=&quot;it-all-started-with-a-light-bulb&quot;&gt;It all started with a light bulb&lt;/h2&gt;
&lt;p&gt;Or to be precise, with the person who invented the light bulb — Thomas Edison.&lt;/p&gt;
&lt;p&gt;Before Thomas Edison and the Industrial Revolution, there was no concept of Job or Workplace. If you were born to a blacksmith — you’d become a blacksmith. Professions were inherited from father to son, and occasionally via an apprenticeship.&lt;/p&gt;
&lt;p&gt;Then came the Industrial Revolution, which brought means of mass production and a Human Workforce. People started to become educated and were able to choose careers.&lt;/p&gt;
&lt;p&gt;And Thomas Alva Edison decided that he needs people to help him with his work. He was self-educated and, rumors say, was not impressed with the knowledge that people acquired through formal education. Thus, he required a way to &lt;em&gt;screen&lt;/em&gt; potential candidates.&lt;/p&gt;
&lt;p&gt;Edison decided to create a test to evaluate candidates&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. His test included questions related to the field Edison was working in, and general knowledge questions on topics such as history and cosmology. The test then leaked in the New York Times&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and thus more companies started to practice a similar process. Fun fact: Albert, yes Einstein, allegedly couldn’t pass this test.&lt;/p&gt;
&lt;h2 id=&quot;modern-days&quot;&gt;Modern Days&lt;/h2&gt;
&lt;p&gt;Back in the modern days, not much has changed. Few studies concluded that the technical interview is useless at predicting job performance&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. While I don’t believe that we should abandon the technical interview altogether, I think I’m in a position to outline a few of the problems with the technical interview and propose alternative solutions.&lt;/p&gt;
&lt;h3 id=&quot;problem-1--brain-teasers&quot;&gt;Problem #1: 🧠 Brain Teasers&lt;/h3&gt;
&lt;p&gt;It’s worth noting that brain-teasers slowly die, and yet some companies continue to practice them. They include questions such as &lt;em&gt;How many ping pong balls you can fit in Boeing 747&lt;/em&gt; or &lt;em&gt;How many cows are in Canada?&lt;/em&gt; — and similar.&lt;/p&gt;
&lt;p&gt;Unless your company is trying to ship ping pong balls via airplanes, or do a count of the cow population in Canada - the primary reason to ask those questions is &lt;em&gt;…to make the interviewer feel smart&lt;/em&gt; - Former Google SVP Laszlo Bock&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&quot;problem-2--on-site-coding--home-assignment&quot;&gt;Problem #2: 👩‍💻 On-site coding / Home assignment&lt;/h3&gt;
&lt;p&gt;This one is still common. Companies are afraid to hire candidates whose coding abilities are mediocre, so they ask them to perform an on-site coding task or submit a coding assessment done at home. There are two main problems with those.&lt;/p&gt;
&lt;h4 id=&quot;on-site-coding-does-not-simulate-a-real-environment&quot;&gt;On-site coding does not simulate a real environment&lt;/h4&gt;
&lt;p&gt;Let’s say you ask the candidate to implement a To-Do app. You provide him with basic scaffolding of an API Server and DAL and give him 1.5 to 2 hours to implement 3 endpoints: add a to-do, complete a to-do and get a list of all the non completed to-dos.&lt;/p&gt;
&lt;p&gt;Now, let’s ask a question — what are you trying to test? What’s the Definition of Done for such an assignment? Successful completion in the allocated time frame? A clean code and maintainable architecture?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I want to see how he writes code under stressful situations!&lt;/em&gt; — you say. And I ask &lt;em&gt;Why? Is your company writing a lot of code under stressful situations?&lt;/em&gt; Then I’d rather move on to the next company.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I want to test his code qualities!&lt;/em&gt; — you tell me. And I ask — &lt;em&gt;By putting the candidate in a constrained environment and limited time frame?&lt;/em&gt; That’s hardly an environment that produces quality code. In a timed environment, such as a production incident, my number one priority is to resolve the problem ASAP and get my product back online so that customers continue to be happy, not to write &lt;em&gt;nice code&lt;/em&gt;, whatever the hell this means. I’d refactor the solution tomorrow when I have time, and I’ve done my postmortem.&lt;/p&gt;
&lt;h4 id=&quot;home-assignment-requires-a-time-investment&quot;&gt;Home assignment requires a time investment&lt;/h4&gt;
&lt;p&gt;As with on-site coding, home assignments goals are vague. What are you trying to test? That the candidate can write a code that adheres to &lt;strong&gt;your&lt;/strong&gt; coding standard? Your coding standards are something that can be learned, not something that the candidate should guess.&lt;/p&gt;
&lt;p&gt;And moreover, home assignments require a time investment. &lt;em&gt;I only want candidates that are willing to show their dedication towards the position&lt;/em&gt;. Of course you do! And candidates want a fair process in which an employer shows dedication towards them, not a home assignment that will take them 2 to 3 hours of unpaid work time, while you will throw a 7 minute glimpse at the code and conclude whether it adheres to your coding standards.&lt;/p&gt;
&lt;h3 id=&quot;do-coding-question-instead&quot;&gt;Do coding question instead&lt;/h3&gt;
&lt;p&gt;Instead of performing an on-site coding that takes 1.5 to 2 hours, or throwing a home assignment — do a coding question.&lt;/p&gt;
&lt;p&gt;The combination I’ve come to love is to design an algorithm and then translate it to code. Let’s say your algorithm is finding whether a given word is a palindrome. You can start by providing a definition:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A palindrome is a word that reads the same backward as forward. Examples of palindromes are &lt;code&gt;madam&lt;/code&gt; or &lt;code&gt;racecar&lt;/code&gt;. Now try to describe an algorithm that will identify if the given word is a palindrome.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The candidate then tries to come up with a solution, while you can guide her if needed. In the end, you ask her to implement the algorithm by providing a basic scaffolding of the function signature such as: &lt;code&gt;function is_palindrome(word: string): boolean&lt;/code&gt; and a set of tests that should all pass once she completed the implementation.&lt;/p&gt;
&lt;p&gt;This combination achieves multiple goals.&lt;/p&gt;
&lt;h4 id=&quot;it-tests-the-candidates-thinking-abilities&quot;&gt;It tests the candidate’s thinking abilities&lt;/h4&gt;
&lt;p&gt;By giving a problem and asking to find a solution, you simulate a real-life scenario in which the candidate requires to think in order to find the right solution. Palindrome is not the best example, but you can come up with any other algorithm (or use Google if you lack imagination). You can understand how the candidate thinks, how he approaches unfamiliar problems.&lt;/p&gt;
&lt;h4 id=&quot;you-can-complicate-it-with-computer-science&quot;&gt;You can complicate it with Computer Science&lt;/h4&gt;
&lt;p&gt;Algorithms can be solved in multiple ways. Some are more performant than others. You can complicate this question by asking the candidate to try to come up with a better complexity. You can ask her how she would implement it in a real-life scenario rather than laboratory conditions. The latter gives you more understanding of tools and technologies she’s familiar with and how she evaluates them for a given problem.&lt;/p&gt;
&lt;h4 id=&quot;you-see-how-she-translates-words-to-code&quot;&gt;You see how she translates words to code&lt;/h4&gt;
&lt;p&gt;Coding is, essentially, translating algorithms and requirements to code. This skill is way more valuable than simply writing clean code. But please use an actual computer and an IDE. Nobody ever writes code on Whiteboard during their career (unless they are University Professors or writing pseudocode).&lt;/p&gt;
&lt;h4 id=&quot;its-simple-and-adaptable&quot;&gt;It’s simple and adaptable&lt;/h4&gt;
&lt;p&gt;Unlike a 1.5-2 hours of on-site coding or home assignment, algorithm implementation has an obvious goal — make the tests pass by implementing the missing function. There is no ambiguity in terms of &lt;em&gt;should I add log calls&lt;/em&gt; or &lt;em&gt;how performant my code should be&lt;/em&gt;. You’ve already discussed the solution, and now you need to implement it. On top of all that — function implementation (which should be limited to a couple of lines of code) is adaptable. It can be done in any language, and in case you are not looking for a “native speaker” of your chosen language, giving him or her option to use a more familiar language — will make the process more smooth.&lt;/p&gt;
&lt;h3 id=&quot;problem-3--manifestation-of-your-superiority&quot;&gt;Problem #3: 👑 Manifestation of your superiority&lt;/h3&gt;
&lt;p&gt;I’ve mentioned this briefly in the Brain Teasers section. Let me do a quick overview of the path from a Trainee to an Interviewer.&lt;/p&gt;
&lt;p&gt;The newly onboarded technical interviewer will be shadowed for a couple of interviews. She starts by being completely quiet and observing her senior counterpart leading the interview. In the next interview, she gains some more confidence and asks a question or two. By third or fourth interview - I, as a Senior Interviewer, take a leap of faith and let her lead the interview by making myself completely quiet (interrupting only if I see that there is a misunderstanding between her and the interviewee).&lt;/p&gt;
&lt;p&gt;At every step, it’s important to have a feedback call right after the interview. I want to understand how she was feeling during the interview, are there any questions. I want to listen to her decision on the interviewee. I want to give her the tools to navigate the interview better. And I want to understand whether she is ready for her first solo interview.&lt;/p&gt;
&lt;h4 id=&quot;the-transition-between-being-a-trainee-to-becoming-an-interviewer&quot;&gt;The transition between being a Trainee to becoming an Interviewer&lt;/h4&gt;
&lt;p&gt;At that point, the well-shadowed trainee interviewer is on his own. It’s now his path and his decisions (I’m always here for him to support and mentor should he need that).&lt;/p&gt;
&lt;p&gt;His first interviews will be a hit or a miss. Occasionally, he will come to you with questions such as &lt;em&gt;I’ve interviewed this engineer, she looks great but lacks X and Y. What do you think?&lt;/em&gt; I’ve been like this at the beginning of my path. But like with anything new, we adapt and start to make decisions on our own. And there comes a time when each of us is going to have to make a choice.&lt;/p&gt;
&lt;h4 id=&quot;either-bring-balance-to-the-force-or-leave-it-in-darkness5&quot;&gt;Either bring balance to the Force or leave it in darkness&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/h4&gt;
&lt;p&gt;Everything about the technical interview can be summarized by one word — &lt;strong&gt;Control&lt;/strong&gt;. The interview is about having control over the outcome of the hiring process. We want to have the control that we won’t hire a candidate that has bad coding standards, is unable to perform architecture design, or is not proactive enough. This is the reason some companies have 3, 4, or even 5 interviews, each focusing on different aspects starting from coding and going to architecture, proactivity, and even personal skills such as conflict resolution, or working in a team.&lt;/p&gt;
&lt;p&gt;And while having control is important, control can also be abused. And the easiest way to abuse control — is to feel superiority. Consider the following, somewhat hypothetical, somewhat real, scenario: An interviewee is coming to your company for a technical interview. He is greeted by 2 senior engineers with grumpy faces who are tired of this process because the last 5 people they interviewed were trying to impose a schema on a by-design schema-less database (Sorry, it’s a personal &lt;a href=&quot;https://www.reddit.com/r/node/comments/2vn542/why_everybody_uses_nodejs_with_mongodb/&quot;&gt;pain point&lt;/a&gt; for me).&lt;/p&gt;
&lt;p&gt;Constant failure brings frustration and builds resentment towards the process. Don’t be like that. Don’t be the person who signals that the potential candidate is a failure just because your last interviews were not successful. Yes, smile. Yes, be nice. The interview is not about you and your frustration with the process. Keep it professional. Remember, behind every candidate might hide a great engineer who can bring value to your company and your product, as well as your team and even yourself.&lt;/p&gt;
&lt;p&gt;When you start asking questions, please remember the following — You’ve asked them hundreds of times and know to navigate yourself from every possible answer and situation. The same is not true about the candidate, avoid making yourself the smartest man in the room. Remember — it’s &lt;strong&gt;not&lt;/strong&gt; about you.&lt;/p&gt;
&lt;h2 id=&quot;the-fix-is-in-framing&quot;&gt;The fix is in framing&lt;/h2&gt;
&lt;p&gt;Recently, I’ve read an &lt;a href=&quot;https://thinkingthrough.substack.com/p/dont-do-interviews-do-discussions&quot;&gt;article&lt;/a&gt; by Mayank Verma about how you should treat the Technical Interview as more of a discussion rather than an interview. The change is not radical — it’s in &lt;a href=&quot;https://en.wikipedia.org/wiki/Framing_(social_sciences)&quot;&gt;framing&lt;/a&gt;. Instead of treating the interview as a process in which you judge the interviewee by his answers, we can turn the interview into a discussion where both the Interviewee and the Interviewer are working towards a joint solution with the purpose of understanding whether the Interviewee is suitable for the position.&lt;/p&gt;
&lt;p&gt;This can take form in different representations. For example: it can be a discussion about the interviewee’s experience. An exercise I like to do is to ask the candidate to pick a feature or an architecture that she considers to be interesting, challenging, or comfortable to discuss. This achieves 2 things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It gives the candidate freedom and control over the interview. Instead of throwing her into the water and asking her to implement some sort of system I’ve designed, I let her talk about something that she did — thus transferring the control from myself to her. This makes the relationship between us equal, instead of Interviewer / Interviewee.&lt;/li&gt;
&lt;li&gt;This gives me a glimpse at what is the most challenging or interesting thing the candidate worked on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I then proceed to a &lt;strong&gt;discussion&lt;/strong&gt;. We discuss who was responsible for this feature, who decided on the architecture, what was her role in that. What would she change now, if she had the time and resources? This gives me way more insight about her as a candidate, instead of a generic design question such as &lt;em&gt;Let’s now design {Some System}&lt;/em&gt;. The former is familiar to the candidate, and she probably knows the in-s and out-s of the architecture; while the latter is… you can’t expect a candidate to do a system design in 1.5 hours and have a &lt;em&gt;great design&lt;/em&gt;. Moreover, the latter might be a hit for someone who worked with systems in that particular domain, and a big miss for someone who did not.&lt;/p&gt;
&lt;h2 id=&quot;-the-manifest-of-a-great-technical-interviewer&quot;&gt;🗒 The Manifest of a Great Technical Interviewer&lt;/h2&gt;
&lt;p&gt;As I’ve mentioned earlier, I’m getting close to the mark of 150 interviewed people. I have an unexplainable passion for interviewing. I have a great track record for post-interview feedback. Furthermore, I learn a lot from the interview as an interviewer, and I think every engineer should try interviewing. Below is a summary of traits that I consider mandatory to become a Great Technical Interviewer.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Be curious&lt;/strong&gt;. By being curious, you arrive at the interview with the purpose to discover. Curiosity allows you to evaluate the candidate without prior biases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate instead of judging&lt;/strong&gt;. By judging, we search for imperfections; by evaluation — we try to find a match. Approach the interview from an evaluation standpoint rather than from a judging one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let go of the ego&lt;/strong&gt;. The interview is not about you or your knowledge. You are not here to show the candidate how awesome you are. Let go of the ego and focus on evaluating the candidate’s skills for the position.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Listen first, only then speak&lt;/strong&gt;. In an ideal interview, the candidate should talk most of the time. Your role is to listen and guide her towards the skills you are looking for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understand his motivation&lt;/strong&gt;. Many candidates submit their CVs to multiple jobs in different domains. Remember that your role is not only to find the best engineer for your team, but to also make sure that your team is the best fit for the candidate. Listen to what he is looking for and be honest with whether you can provide that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be an expert in your field (or admit your lack of knowledge)&lt;/strong&gt;. If you are evaluating a candidate for great RDBMS knowledge, then you should be close to an expert in that field. Finding yourself arguing with a candidate on a specific matter, only to later find out that she was right, and you were wrong, due to lack of knowledge, — is an unpleasant situation to be in. If in doubt, you can always say &lt;em&gt;I don’t know, but I’ll definitely check that after the interview&lt;/em&gt; (and obviously go and check).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seek to learn&lt;/strong&gt;. Every person I’ve interviewed — taught me something new. It might be filling a technical gap I’ve had or acquiring new communication skills. Always seek to learn, even from candidates that do not pass your interview.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Have a structure, but know to be flexible&lt;/strong&gt;. You must lead the interview. It should have structure. It should evaluate the candidate based on the position you are interviewing for. But you should also remember that everyone is unique, and occasionally, you will encounter candidates with a unique experience that will break your interview structure. You must know how to navigate yourself back towards the qualities you are looking for, without making the process feel &lt;em&gt;robotic&lt;/em&gt; or &lt;em&gt;scripted&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accept the fact that you are going to fail&lt;/strong&gt;. No matter how great you are as an interviewer, eventually you will miss a great engineer or hire an unsuitable one. Your goal should be to minimize those mistakes, but you should accept the fact that they will happen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mentor others&lt;/strong&gt;. When you feel comfortable with the interview, mentor other developers so they, too, will become Great Technical Interviewers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;this-path-is-hard-but-it-is-rewarding&quot;&gt;This path is hard, but it is rewarding&lt;/h2&gt;
&lt;p&gt;Interviewing is a hard process. There are times when the hiring is on fire, and you will have 2-3 interviews a week, each taking 1.5 to 2 hours. Don’t overload yourself. Pushback if you feel you’ve had too many interviews and need a rest. Always continue to improve yourself and the process. Questions the existing status quo — always remember that the status quo became the status quo because someone questioned it. And enjoy the process — it’s way more fulfilling once you do. Good luck, young Padawan.&lt;/p&gt;
&lt;h2 id=&quot;bonus--tips-for-candidates-from-an-interviewers-perspective&quot;&gt;Bonus — Tips for candidates from an Interviewer’s perspective&lt;/h2&gt;
&lt;p&gt;Once I became an interviewer, I’ve realized some things that I wish I knew when I was an interviewee. Below is a summary of them.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Embrace rejections&lt;/strong&gt;. Rejection is not necessarily a red mark that says &lt;em&gt;you are not a good engineer&lt;/em&gt;. Rejection simply means &lt;em&gt;the qualities you possess do not suit &lt;strong&gt;our&lt;/strong&gt; position&lt;/em&gt;. I’ve interviewed talented engineers that I’ve had to reject because they were not suitable for the position.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always ask for feedback&lt;/strong&gt;. I was surprised how few engineers ask for post-rejection feedback. If you are interviewing for a company and get rejected — always ask for honest feedback. Don’t just settle on &lt;em&gt;We’ve decided to proceed with other candidates&lt;/em&gt;. Ask what you can improve so that the next time you apply — you will succeed. (Not all companies are willing to provide such feedback. If you request one and get a generic response or no response, consider yourself lucky — you’ve avoided a workplace that isn’t concerned with your development).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid biased information&lt;/strong&gt;. Your CV is not your Facebook page. You think that the fact that you play the guitar is cute — but we are all humans, and we can be biased. You want to avoid blowing your interview just because you fell on an interviewer who prefers drums to guitar. Interviewers and HR does a lot of work to eliminate bias from our decision-making, but we are all humans. Avoid including any personal information in your CV.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interview the company&lt;/strong&gt;. Remember, the interview is not only about them interviewing you — but it’s also about you interviewing the company. Make a list of things that are important to you in a company, and make sure to ask questions and understand whether this particular company can provide what you are looking for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t be afraid to say &lt;em&gt;I don’t know&lt;/em&gt;&lt;/strong&gt;. I’d have more respect towards a candidate that says “&lt;em&gt;I don’t know, I never worked with X. I’d take some time and read about it&lt;/em&gt;”, instead of a candidate that will try to confuse me with buzzwords.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Have fun&lt;/strong&gt;. Treat interviews as games. See how well you can pass them and what you can learn from them.&lt;/li&gt;
&lt;/ol&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.businessinsider.com/evolution-of-the-job-interview-2015-5&quot;&gt;Business Insider - The Evolution of the Job Interview&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nytimes.com/1921/05/11/archives/edison-questions-stir-up-a-storm-victims-of-test-say-only-a-walking.html&quot;&gt;The New York Times 1921-05-11 - Edison Questions Stir up a Storm of Victims&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nytimes.com/2017/04/08/opinion/sunday/the-utter-uselessness-of-job-interviews.html&quot;&gt;The New York Times - The Utter Uselessness of Job Interviews&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.theatlantic.com/business/archive/2013/06/google-finally-admits-that-its-infamous-brainteasers-were-completely-useless-for-hiring/277053/&quot;&gt;The Atlantic - Google Finally Admits That Its Infamous Brainteasers Were Completely Useless for Hiring&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.quotes.net/mquote/91307&quot;&gt;Obi-Wan to Darth Vader&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Supercharge Your NodeJS With Rust</title><link>https://kudmitry.com/articles/supercharge-nodejs-with-rust/</link><guid isPermaLink="true">https://kudmitry.com/articles/supercharge-nodejs-with-rust/</guid><pubDate>Sat, 23 Oct 2021 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Node isn’t the fastest framework out here. It’s not the slowest either, v8 is doing wonders to its speed, but nevertheless, if we setup an unfair battle between Node and say Rust; Node will lose.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you are interested to compare this approach to WebAssembly, I’ve written a new &lt;a class=&quot;internal new&quot; href=&quot;/articles/native-rust-wasm/&quot;&gt;article&lt;/a&gt; on WASM in comparison to native modules.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-rust&quot;&gt;What is Rust?&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;Rust is a multi-paradigm, high-level, general-purpose programming language designed for performance and safety, especially safe concurrency. Rust is syntactically similar to C++, but can guarantee memory safety by using a borrow checker to validate references.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Rust_(programming_language)&quot;&gt;Wikipedia&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;Rust is a strongly typed and compiled language. It supports a lot of, so called, modern features such as closure and anonymous functions, a rich standard library, types and polymorphism as well as modern tooling such as Cargo which is an equivalent to &lt;code&gt;npm&lt;/code&gt; in the world of Node.&lt;/p&gt;
&lt;p&gt;Rust is also Memory Safe language. All &lt;strong&gt;safe&lt;/strong&gt; Rust code is memory checked during compilation so unless you opt to use unsafe Rust, you won’t encounter the famous &lt;code&gt;Segmentation Fault&lt;/code&gt; you’d get in C or C++ by accessing a pointer that is pointing to a memory you no longer own.&lt;/p&gt;
&lt;p&gt;All this makes Rust a very attractive replacement to C/C++.&lt;/p&gt;
&lt;h2 id=&quot;ok-great-and-all-but-i-write-javascript&quot;&gt;Ok great and all, but I write Javascript!&lt;/h2&gt;
&lt;p&gt;Yeah, me too. And here comes the great part. We can use all the beauty of Rust in Javascript!&lt;/p&gt;
&lt;p&gt;Oftentimes we encounter a situation when we need to run a heavy task such as PDF generation or some sort of computation. Most of the modules in NPM are Javascript based, meaning they will suffer a penalty at runtime. Usually in such situation we opt to “creative” solutions such as offloading the heavy process to a Lambda function and turning the entire process into asynchronous for the user. And yet, I rarely hear people suggest or even evaluate the solution of embedding a native module that most of the times will be faster.&lt;/p&gt;
&lt;p&gt;Another great usage for Rust in the Javascript ecosystem - is providing shared backend for Desktop applications. Let’s say you are developing an MVP in Electron and want to use SQLite for data storage. While you can use the &lt;a href=&quot;https://www.npmjs.com/package/sqlite3&quot;&gt;sqlite3&lt;/a&gt; package which is native C++, you can also create a dynamic library (also knows as &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt; file in Linux and Windows respectively) in Rust. What this gives you, is the ability to later ditch Electron in favor of Native Framework for each OS while doing 0 changes to your Backend code.&lt;/p&gt;
&lt;h2 id=&quot;so-how-do-i-use-rust-from-javascript&quot;&gt;So how do I use Rust from Javascript?&lt;/h2&gt;
&lt;p&gt;I’m glad you’ve asked! Please meet &lt;code&gt;Neon&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/neon.png&quot; alt=&quot;Neon Bindings#774x300&quot;&gt;&lt;figcaption&gt;Neon Bindings#774x300&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://neon-bindings.com/&quot;&gt;Neon&lt;/a&gt; is a library and toolchain for embedding Rust in your Node.js applications and libraries. It is similar to creating native modules with C/C++ but with none of the fear and headaches associated with unsafe systems programming.&lt;/p&gt;
&lt;p&gt;Let’s try now to create a native Fibonacci function in Rust and call it from Node. Disclaimer: This is not a tutorial on Rust, so I’m not going to explain Rust specifics here. You can always go to the &lt;a href=&quot;https://doc.rust-lang.org/book/&quot;&gt;Rust Book&lt;/a&gt; to learn about Rust, however the syntax I’m gonna use here should be easy to understand to anyone familiar with Typescript, or any other statically typed language like C/C++, Java, Kotlin or C#. So let’s dive in.&lt;/p&gt;
&lt;h3 id=&quot;step-1---setup-a-project&quot;&gt;Step 1 - Setup a project&lt;/h3&gt;
&lt;p&gt;Let’s start by creating a Rust project. We will start by creating &lt;code&gt;Cargo.toml&lt;/code&gt; file. It’s a file that mimics the role of &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fibonacci_rs&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;edition&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;2018&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;crate-type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cdylib&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dependencies&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;0.9&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;default-features&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;features&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;napi-6&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we define our package. We give it a name, we define the version and what edition of Rust we are going to use.&lt;/p&gt;
&lt;p&gt;Then we define the type of our package. Remember, Rust is a compiled language and it can produce all kinds of binary outputs. They could be executables (such as &lt;code&gt;.exe&lt;/code&gt; files in Windows) or they could be library files such as &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt;. In our case we create a library, hence the &lt;code&gt;[lib]&lt;/code&gt; declaration. There are 2 types of libraries that most languages (including Rust) can produce: &lt;code&gt;dynamic&lt;/code&gt; and &lt;code&gt;static&lt;/code&gt;. In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static libraries usually have the extension of &lt;code&gt;.a&lt;/code&gt; or &lt;code&gt;.lib&lt;/code&gt; in Linux/MacOS and Windows respectively, Static libraries are compiled &lt;strong&gt;into&lt;/strong&gt; the final executable. So if I produce a static library and then link my executable against it - the final output will be one file, where the static library is embedded into the executable itself. Their main purpose is to create reusable code but once the binary is compiled, you can’t change the implementation of a static library without recompiling the binary.&lt;/li&gt;
&lt;li&gt;Dynamic libraries have the extensions of &lt;code&gt;.so&lt;/code&gt;, &lt;code&gt;.dylib&lt;/code&gt; and &lt;code&gt;.dll&lt;/code&gt; in Linux, Mac and Windows respectively. The difference is that dynamic libraries &lt;strong&gt;are not&lt;/strong&gt; compiled into the final binary file and instead are loaded at runtime by the OS. So when I link my binary against a dynamic library, I’ll have 2 files in the output - the dynamic library and my binary. Their main use is to create a reusable code between multiple applications without locking the implementation. Also, since dynamic libraries are not compiled into the executable, I can reuse their code by having any number of executables that depend on the same dynamic libraries. Libraries such as Microsoft C/C++ runtime are dynamic libraries that are downloaded once by the user to their machine, and then used by countless applications that depend on MSVC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since we are not going to recompile Node.js itself, we are interested in producing a dynamic library that can be linked at runtime, rather than embedded directly into Node.js runtime. Therefore we define our &lt;code&gt;crate-type&lt;/code&gt; as &lt;code&gt;cdylib&lt;/code&gt; meaning that we want to create a &lt;strong&gt;d&lt;/strong&gt;ynamic &lt;strong&gt;lib&lt;/strong&gt;rary with C ABI (hence the “c” in the beginning). I’m not going to go into details what ABI is and what’s its purpose, I’ll instead drop a link to &lt;a href=&quot;https://en.wikipedia.org/wiki/Application_binary_interface&quot;&gt;Wikipedia&lt;/a&gt;. Note that we can also create &lt;code&gt;dylib&lt;/code&gt; which will have unstable Rust ABI that can be changed across Rust releases.&lt;/p&gt;
&lt;p&gt;Last but not least - we define our dependencies. In our case, we depend only on &lt;code&gt;neon&lt;/code&gt; library.&lt;/p&gt;
&lt;h3 id=&quot;step-2---write-our-rust-fibonacci&quot;&gt;Step 2 - Write our Rust Fibonacci&lt;/h3&gt;
&lt;p&gt;Since we are creating a rust library, we need to create the following file in the root of our project: &lt;code&gt;src/lib.rs&lt;/code&gt;. In this file we are going to create our Fibonacci function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) + &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A simple, recursive function that accepts a 32bit integer and returns a 32bit integer.&lt;/p&gt;
&lt;h3 id=&quot;step-3---create-a-binding-for-our-rust-fibonacci-into-javascript&quot;&gt;Step 3 - Create a binding for our Rust Fibonacci into Javascript&lt;/h3&gt;
&lt;p&gt;Currently, we’ve created a Rust function. We can run &lt;code&gt;cargo build&lt;/code&gt; in order to compile our code. This will download and compile &lt;code&gt;neon&lt;/code&gt; and any of its dependencies, and will compile our &lt;code&gt;lib.rs&lt;/code&gt; file. We will get a compilation warning saying that &lt;code&gt;fn fibonacci&lt;/code&gt; is dead code, since no body uses it, but nevertheless, if you’ve done everything correctly, you should end up with a new directory tree of &lt;code&gt;target/debug&lt;/code&gt; and inside it you will find a file named &lt;code&gt;libfibonacci_rs.{dylib,so,dll}&lt;/code&gt; (the extension will depend on what type of OS you are compiling on).&lt;/p&gt;
&lt;p&gt;Since its a dynamic library that does not extern any functionality, it is now useless. But let’s put some Neon lights onto it. Edit &lt;code&gt;src/lib.rs&lt;/code&gt; and add the following function:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FunctionContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;types&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) + &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci_api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FunctionContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;argument&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;	Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve added some &lt;code&gt;use neon&lt;/code&gt; statements. They are similar to &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;require&lt;/code&gt; statements in Javascript. We’ve also added a new function called &lt;code&gt;fibonacci_api&lt;/code&gt;. This function is the conversion layer between Javascript and Rust. It accepts a &lt;code&gt;FunctionContext&lt;/code&gt; since it’s a function and returns a &lt;code&gt;JsResult&lt;/code&gt; of &lt;code&gt;JsNumber&lt;/code&gt; meaning that on successful execution, it will return a value that is equivalent to &lt;code&gt;number&lt;/code&gt; type in Javascript.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cx&lt;/code&gt; is used to interface with the Javascript world. We first get a &lt;code&gt;Handle&lt;/code&gt; to &lt;code&gt;JsNumber&lt;/code&gt; which is the first (index 0) argument that was passed to our function. &lt;code&gt;Handle&lt;/code&gt; is a safe reference to a Javascript value that is owned and managed by the Javascript engine’s memory management. Since Javascript is a dynamic language and Rust is statically typed, we need to manually downcast the javascript value to a number. Since &lt;code&gt;fibonacci_api&lt;/code&gt; can be called with anything (a string, an object or array) from Javascript, this downcast can fail, therefore we need to handle a situation where it fails. In my example, I just &lt;code&gt;.unwrap&lt;/code&gt; the result of the downcast, since this is a tutorial and not a real world example, but in real world you’d need to handle a situation where the first argument is not a &lt;code&gt;JsNumber&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Just a few more words on values and down/up casting and we will continue. Everything you get in Rust via Neon, initially has the type of &lt;code&gt;JsValue&lt;/code&gt;. &lt;code&gt;JsValue&lt;/code&gt; is sort of a catch all type that can be anything: &lt;code&gt;JsNumber&lt;/code&gt;, &lt;code&gt;JsObject&lt;/code&gt;, &lt;code&gt;JsNull&lt;/code&gt;, &lt;code&gt;JsBuffer&lt;/code&gt; and etc. A &lt;code&gt;Handle&lt;/code&gt; with &lt;code&gt;JsValue&lt;/code&gt; can be downcasted to a specific type by using &lt;code&gt;Handle::downcast()&lt;/code&gt; or upcasted by using &lt;code&gt;Handle::upcast()&lt;/code&gt;. There is a nice diagram on the &lt;a href=&quot;https://docs.rs/neon/0.9.1/neon/types/index.html&quot;&gt;&lt;code&gt;neon::types&lt;/code&gt; Documentation&lt;/a&gt; that shows the relation between &lt;code&gt;JsValue&lt;/code&gt; and other Javascript types. I’ll include it here:&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/types.jpg&quot; alt=&quot;javascript type hierarchy#960x480&quot; title=&quot;The Javascript Type Hierarchy&quot;&gt;&lt;figcaption&gt;javascript type hierarchy#960x480&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Anyway, back to the code.&lt;/p&gt;
&lt;p&gt;So now we have a &lt;code&gt;Handle&amp;#x3C;JsNumber&gt;&lt;/code&gt;. We can access the internal value by calling &lt;code&gt;Handle::value()&lt;/code&gt; method, which in case of a &lt;code&gt;JsNumber&lt;/code&gt; handle, returns &lt;code&gt;f64&lt;/code&gt; (since &lt;code&gt;number&lt;/code&gt; in Javascript is represented as float), but we safely convert it to &lt;code&gt;i32&lt;/code&gt; for our Fibonacci function.&lt;/p&gt;
&lt;p&gt;We then safely return the result as &lt;code&gt;JsNumber&lt;/code&gt; by calling the &lt;code&gt;.number()&lt;/code&gt; method of the &lt;code&gt;Function Context&lt;/code&gt;. And we are done.&lt;/p&gt;
&lt;p&gt;Once again, we can compile the code by running &lt;code&gt;cargo build&lt;/code&gt;. It will once again warn us about dead code, but this time it will mark both &lt;code&gt;fibonacci&lt;/code&gt; and &lt;code&gt;fibonacci_api&lt;/code&gt; function as dead code, since no body calls them.&lt;/p&gt;
&lt;h3 id=&quot;step-4---export-our-rust-function-to-javascript-world&quot;&gt;Step 4 - Export our Rust function to Javascript world&lt;/h3&gt;
&lt;p&gt;We are almost done with Rust. Bear with me for a little bit more.&lt;/p&gt;
&lt;p&gt;Right now we have a function that converts a JS call into Rust call, but we have no way to call that function from Javascript. Enter the &lt;code&gt;main&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;If you are familiar with C or C++, every executable has a &lt;code&gt;main&lt;/code&gt; function. This is a special type of function that the OS will run once it loads your binary into memory. Libraries however do not have &lt;code&gt;main&lt;/code&gt; functions, since libraries are not meant to be executed by themselves. However you’ve probably seen a Javascript module. It usually ends with &lt;code&gt;module.exports = {}&lt;/code&gt;. We can treat it as &lt;code&gt;main&lt;/code&gt;. And that’s what Neon does.&lt;/p&gt;
&lt;p&gt;Once again, edit our &lt;code&gt;src/lib.rs&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FunctionContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;types&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; neon&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NeonResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		n&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;		_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; =&gt; &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) + &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci_api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FunctionContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;argument&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;::&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;JsNumber&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;unwrap&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; i32&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;	Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;#[neon::main]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NeonResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;()&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;	cx&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;export_function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fibonacci_rs&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fibonacci_api&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)?;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;	Ok&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve added a small function named &lt;code&gt;main&lt;/code&gt; and marked it with &lt;code&gt;neon::main&lt;/code&gt; attribute to tell Neon that this is the main entry of the module. This function also accepts a context, but not &lt;code&gt;FunctionContext&lt;/code&gt; like our previous function, instead it accepts &lt;code&gt;ModuleContext&lt;/code&gt;. While function context is used to interface with js functions and have operation like getting arguments of a function, up/down casting &lt;code&gt;JsValue&lt;/code&gt;s into specific types and generating results; Module context is used to interface with Javascript modules, and the main methods in &lt;code&gt;ModuleContext&lt;/code&gt; are &lt;code&gt;ModuleContext::export_function()&lt;/code&gt; and &lt;code&gt;ModuleContext::export_value()&lt;/code&gt;, which are used to export functions and values from a module.&lt;/p&gt;
&lt;p&gt;In our case we export a function and name it &lt;code&gt;fibonacci_rs&lt;/code&gt;, while the actual implementation of it is &lt;code&gt;fibonacci_api&lt;/code&gt; we’ve written earlier.&lt;/p&gt;
&lt;p&gt;In Javascript world, this code would look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  fibonacci_rs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fibonacci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;cargo build&lt;/code&gt; once again will produce our library without any warnings. We are now done with Rust, let’s move to Javascript.&lt;/p&gt;
&lt;h3 id=&quot;step-5---fibonacci-javascript&quot;&gt;Step 5 - Fibonacci Javascript&lt;/h3&gt;
&lt;p&gt;Let us now create a new Javascript file: &lt;code&gt;fibonacci.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; process&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;argv&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; parseInt&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;isNaN&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Provided value is not a number&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a simple file that reads the first argument we pass to it from CLI, validates that it is a number and prints it back. You can execute it by &lt;code&gt;node fibonacci.js 10&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;Now we somehow need to call the fibonacci function we’ve created in Rust. For that we will have to create a &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fibonacci_rs&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;version&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;private&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;fibonacci.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;scripts&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;build&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;dependencies&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &quot;cargo-cp-artifact&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;^0.1.5&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &quot;devDependencies&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; to install the dependency and then run &lt;code&gt;npm run build&lt;/code&gt; to generate the native module. If everything worked correctly, you should end up with &lt;code&gt;index.node&lt;/code&gt; file in your root directory.&lt;/p&gt;
&lt;p&gt;Let’s explain. &lt;code&gt;cargo-cp-artifact&lt;/code&gt; is a package maintained by Neon team. What this package does, is handling the extraction of the library produced by &lt;code&gt;cargo build&lt;/code&gt;. Remember, &lt;code&gt;cargo build&lt;/code&gt; generates different files depending on the host OS. In my case it generated &lt;code&gt;libfibonacci_rs.dylib&lt;/code&gt; since I’m on Mac, but it could also generate &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt; files if you are on Linux or Windows. Theoretically, you can go manually to the &lt;code&gt;target&lt;/code&gt; directory and copy the library file to the root of the project and name it &lt;code&gt;index.node&lt;/code&gt;, but &lt;code&gt;cargo-cp-artifact&lt;/code&gt; is a handy utility to do that for you.&lt;/p&gt;
&lt;p&gt;Also note: &lt;code&gt;index.node&lt;/code&gt; is not a special name. You can call it &lt;code&gt;what.ever&lt;/code&gt;, but the &lt;code&gt;.node&lt;/code&gt;extension is used to represent a native Node.js module.&lt;/p&gt;
&lt;p&gt;Now lets go back to our &lt;code&gt;fibonacci.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;fibonacci_rs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;./index.node&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; process&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;argv&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; parseInt&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;isNaN&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Provided value is not a number&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; fibonacci_rs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can run it with &lt;code&gt;node fibonacci.js 10&lt;/code&gt; and you should get the result of &lt;code&gt;55&lt;/code&gt;. &lt;strong&gt;Note:&lt;/strong&gt; do not input high numbers. Since this is a very inefficient and recursive implementation of Fibonacci, it will hang on numbers above 40ish.&lt;/p&gt;
&lt;p&gt;Let’s explain. We require our &lt;code&gt;index.node&lt;/code&gt; as if it was a regular Javascript module. We know that it exports only 1 function: &lt;code&gt;fibonnaci_rs&lt;/code&gt; since that’s what we did in &lt;code&gt;cx.export_function(&quot;fibonacci_rs&quot;, fibonacci_api)?;&lt;/code&gt; in &lt;code&gt;src/lib.rs&lt;/code&gt; , hence we require it and then execute it.&lt;/p&gt;
&lt;p&gt;That’s it! We’ve created a native module in Rust.&lt;/p&gt;
&lt;p&gt;You can clone the entire code of this tutorial from my &lt;a href=&quot;https://github.com/skwee357/node-rust-supercharge&quot;&gt;GitHub&lt;/a&gt;. You can also explore the &lt;a href=&quot;https://github.com/neon-bindings/examples&quot;&gt;examples repository&lt;/a&gt; by Neon for more robust examples like &lt;code&gt;gzip-stream&lt;/code&gt; or &lt;code&gt;async sqlite&lt;/code&gt;. Feel free to explore the &lt;a href=&quot;https://github.com/neon-bindings/neon&quot;&gt;neon repo&lt;/a&gt; as well, they also have a link to their Slack channel for questions and support.&lt;/p&gt;
&lt;h2 id=&quot;what-you-can-do-with-it&quot;&gt;What you can do with it?&lt;/h2&gt;
&lt;p&gt;Many things. You can access native implementations that are not available in Node. You can use any Rust &lt;code&gt;crate&lt;/code&gt; (equivalent to &lt;code&gt;npm&lt;/code&gt; package) from &lt;a href=&quot;https://crates.io/&quot;&gt;crates.io&lt;/a&gt; (provided you implement the required neon bindings), or you can have another strong tool to evaluate when your Javascript implementation is not fast enough for your needs. I’m myself currently experimenting in writing a Desktop application using Electron for the Frontend and Rust for the Backend. This allows me to isolate my backend code into a reusable library that I can plug into any more performant UI Framework such as Qt, GTK and SwiftUI, as well as letting me access SQLite using native sqlite implementation written in C rather than Javascript.&lt;/p&gt;
&lt;h2 id=&quot;things-to-take-into-account&quot;&gt;Things to take into account&lt;/h2&gt;
&lt;p&gt;If you are not coming from the world of compiled languages, or you knowledge of it became rusty (no pun intended), you might need to pay attention to some caveats with using native modules written in any language and not just Rust.&lt;/p&gt;
&lt;p&gt;With Javascript, Python or Ruby, I can give you a code and you can run it (provided you have the runtime installed). Dynamic languages are easily shared between people and machines. Compiled languages are not. If I give you my binary &lt;code&gt;index.node&lt;/code&gt; from this tutorial, you might or might not be able to use it. It depends on the host OS you are running. The safe bet is to compile the code yourself on the OS that you intend to run the code. This means that native modules should not be committed into your source control, but instead be built during your CI/CD. And they &lt;strong&gt;must&lt;/strong&gt; be built on the same OS that they are going to be executed on. So if your Node application is running in Alpine Linux Docker container, it is very much advised that you compile your native modules on the same docker image.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s it for today. I hope this newly acquired knowledge will help you to become a better Node developer.&lt;/p&gt;</content:encoded></item><item><title>10 pushups every morning, for 30 days. September Challenge</title><link>https://kudmitry.com/articles/10-push-up-every-morning-for-30-days-september-challenge/</link><guid isPermaLink="true">https://kudmitry.com/articles/10-push-up-every-morning-for-30-days-september-challenge/</guid><pubDate>Fri, 01 Oct 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On 31st of August I was like “Hey! It’s almost September, maybe I should do a challenge?”, and after some time I’ve settled on 10 push-ups every morning for the entire months.&lt;/p&gt;
&lt;p&gt;My relationship with sports was always a love-hate relationship. There have been periods where I was hitting the gym consistently and seeing results, and periods where I’d be like “damn, that belly!”. And while now I train regularly in boxing, I didn’t do that challenge for the sake of getting ripped or any other physical health benefits.&lt;/p&gt;
&lt;p&gt;And before we start, as always: I’m not a doctor, consult with one first. Especially if you have physical problems.&lt;/p&gt;
&lt;h2 id=&quot;make-your-bed&quot;&gt;Make your bed&lt;/h2&gt;
&lt;p&gt;In the self-improvement community, there is this saying that the first thing you should do after you wake up is - making your bed. I think it started from a book by Ex-Navy seal, Admiral William H. McRaven. A big disclaimer - I did &lt;strong&gt;not&lt;/strong&gt; read the book.&lt;/p&gt;
&lt;p&gt;There are multiple reasons for making your bed in the morning: attention to details, discipline, and a reminder at the end of the day that you’ve achieved something - even if it’s small.&lt;/p&gt;
&lt;p&gt;You see, when you wake up you usually rush through your morning routine - pee, wash your face, clean your teeth, grab some coffee, get into a car, arrive to work. By the time you start working you’ve already spent a good portion of the day. And if your day is filled with meaningless meetings - by the end of it you find yourself sitting on the couch and thinking: &lt;em&gt;Damn! The whole day passed by, and I haven’t done anything significant&lt;/em&gt;. Therefore - make your bed in the morning.&lt;/p&gt;
&lt;p&gt;I wasn’t born in the US, nor I ever lived there. I, however, was raised in a discipline focused family, so making my bed in the morning wasn’t something unusual for me. No matter the circumstances, I’d always make my bed in the morning. So I wasn’t able to see any benefits from making my bed, but I wanted to see if I can get benefits in sport.&lt;/p&gt;
&lt;p&gt;Right now, my relationship with sport is more of a hate than love. I have my boxing sessions once a week, but It’s not enough. I want to try yoga, I want to go swimming and I want to do weight lifting. And every day I’m not doing them, I find myself thinking &lt;em&gt;Damn! The whole day just passed by and I haven’t done any sports&lt;/em&gt;. So I decided to twist the “make your bed” into “do something active” and see if I’d have the same benefits. Those are my findings.&lt;/p&gt;
&lt;h3 id=&quot;i-hate-mornings&quot;&gt;I hate mornings&lt;/h3&gt;
&lt;p&gt;I’m not a morning person. Some mornings are better, but most of them are not that great. I’m usually tired, unfocused and mainly working on automatic gear. Wake up, drink water, pee, brush teeth, food, coffee, work. I didn’t have any problems adding another activity such as “pushups” but I found out that it is physically demanding. I can do 10 pushups easily at any moment during the day - but not right after waking up. My body is not warmed up, my mind is frozen.&lt;/p&gt;
&lt;p&gt;I know there are people who like mornings. They are energetic, can do sports in the morning, discuss philosophical topics and do deep work. I on the other hand believe I’m more of a “midday owl”. I need those few hours in the morning to boot up correctly. And doing pushups felt like some sort of punishment to my, already hard, mornings.&lt;/p&gt;
&lt;h3 id=&quot;its-not-enough&quot;&gt;It’s not enough&lt;/h3&gt;
&lt;p&gt;I wasn’t expecting to be Arnold by October from just doing 10 pushups a day. But nevertheless 10 is better than 0. Obviously I haven’t seen any benefits from it - it was not easier after 20 days, I did not have more muscle and I haven’t felt any health improvements. This is a reminder to always be realistic.&lt;/p&gt;
&lt;p&gt;I’m a big believer in taking baby steps towards new habits, especially if you have problems committing, because as I’ve talked in &lt;a class=&quot;internal new&quot; href=&quot;/articles/compound-effect-and-habits/&quot;&gt;my post&lt;/a&gt; regrading habits - you get compound interest on them. And compound interest on 0 is always 0. So you have to start. 1 page a day is better. By the end of the year you’d read 365 pages, as opposed to 0. But 365 pages is barely a book, so don’t fool your self with “read 50 books” new year resolutions.&lt;/p&gt;
&lt;h3 id=&quot;there-is-no-reflection&quot;&gt;There is no reflection&lt;/h3&gt;
&lt;p&gt;The main reason I’ve done 10 pushups a day was to get a positive reflection at the end of the day. When I find myself sitting at the end of the day and reflecting on it, so that I could say “Yeah, I did 10 pushups. It’s small, but it’s an achievement”, and honestly - it never happened.&lt;/p&gt;
&lt;p&gt;There was no single day when I was even remembering that fact that I did 10 pushups in the morning. It was something automatic that I’ve just plugged into my morning routine. I was not excited about the fact that I brush my teeth or make my bed, nor I was praising myself for doing 10 pushups in the morning.&lt;/p&gt;
&lt;p&gt;And while I don’t want to debunk the entire movement of “reminder that you’ve achieved something” - it did not work for me. Maybe if you are a messy person, who have troubles to bring order to the physical world around you - then yes, start with something as small as making your bed - might do wonders to you. At least I believe you must try it and see for yourself.&lt;/p&gt;
&lt;p&gt;However, in my experience, if you’ve already climbed higher in your physical or mental needs, doing small things such as 10 pushups a day, won’t make you love yourself by the end of the day. I was doing sport consistently. I know I’m capable of more than 10 pushups a day. I’m a lazy fuck and big procrastinator and I need to work on that rather than fooling myself with small achievements such as 10 pushups a day.&lt;/p&gt;
&lt;p&gt;And this is yet another reminder to myself - everyone is different. Things that might work for me, might not work for you and vise-versa. This is the reason I’m doing those challenges - because the best way to learn is to try it yourself. Get inspiration from books and YouTube videos, but always try yourself and see if you have any benefits from it. And if not - forget it, find something new and try again. And iterate on that process.&lt;/p&gt;
&lt;p&gt;If you have suggestions to what challenges I can do next, don’t hesitate to &lt;a href=&quot;https://twitter.com/JikoKaizener&quot;&gt;Tweet Me&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Who are you?</title><link>https://kudmitry.com/articles/who-are-you/</link><guid isPermaLink="true">https://kudmitry.com/articles/who-are-you/</guid><pubDate>Mon, 20 Sep 2021 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you know who are you? Have you ever asked yourself this question and came up with an answer?&lt;/p&gt;
&lt;p&gt;I never lived in the medieval times, nor I know someone who did, but I have this image in my head. Imagine a medieval blacksmith. He has this small, two-story house. He lives with his wife and two kids on the second floor, while the first floor is converted to be his shop. He crafts swords here. He has some livestock. Cows that they milk. Sheep that they grow. Horse that he uses to go to the nearby friend of his - a miner, from which he buys his steel in order to craft his swords and later sell them to warriors of his village.&lt;/p&gt;
&lt;p&gt;This blacksmith doesn’t compare his house to the house of his miner friend. He doesn’t compare his horse to the horse of his neighbor - the alchemist. And yet - he lives a happy life.&lt;/p&gt;
&lt;h2 id=&quot;weve-lost-our-true-self&quot;&gt;We’ve lost our true self&lt;/h2&gt;
&lt;p&gt;We, on the other hand, do compare ourselves. Who dresses better, who drives a better car. Some of us compare themselves to others, while some show-off their “superiority” by attracting attention to their material possession - A Turquoise Louis Vuitton T-Shirt that costs &lt;a href=&quot;https://en.louisvuitton.com/eng-nl/products/inside-out-t-shirt-nvprod2800288v#1A8R5E&quot;&gt;470 Euro&lt;/a&gt; or revving the engine of their Lamborghini. And on top of all this, we are being bombarded by brands, through advertisement, that we must get that new and shiny pair of sneakers.&lt;/p&gt;
&lt;p&gt;And hence, we’ve lost ourselves. We no longer know who we are and what our values are. And it seems like every person is better than us at something. He is a better photographer, she is a better stylist, they have a better house. And we want all of it.&lt;/p&gt;
&lt;h3 id=&quot;im-too-busy-for-that&quot;&gt;I’m too busy for that&lt;/h3&gt;
&lt;p&gt;One of the reasons I &lt;em&gt;think&lt;/em&gt; that the blacksmith from my story is happy with what he has, is because he is doing the craft the likes. He doesn’t have time to think about that alchemist neighbor of his, there is a fucking war coming, and he needs to craft his best swords, because he is the best blacksmith in town.&lt;/p&gt;
&lt;p&gt;We, on the other hand, are, what I call, pseudo-busy. We never have time for anything. Always working hard. &lt;em&gt;Nah man, too busy this week, let’s try in 3 months I might have a window of 27 minutes in there.&lt;/em&gt; And yet, procrastination is flourishing, escapism from day to day job is rising. Everybody wishes to be on a tropical island instead of their office cubicle. We are - pseudo busy. We try to look busy, because busyness is associated with business. And business is cool.&lt;/p&gt;
&lt;p&gt;But we are not really busy. Or not in the sense of being in the flow and doing what we like and enjoy the most. Hence, we procrastinate, escape in our mind to our next vacation, while simultaneously scrolling through social media - to eventually realize that it’s the end of the day already, I barely did anything today, and yet I had no to time to call my friends or family, nor buy any groceries. &lt;em&gt;Jeez, I was sooo busy today.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;there-is-too-much-information&quot;&gt;There is too much information&lt;/h3&gt;
&lt;p&gt;The other reasons I think my blacksmith is happy, is because he probably didn’t choose this life. He was born to a blacksmith, inherited the shop together with horse and sheep, added second floor to the house with materials he could afford and probably never seen anything outside his village.&lt;/p&gt;
&lt;p&gt;We, on the other hand, have access to the entire knowledge of humanity. Even if we’ve never been to a specific country - we can Google how it looks, find what’s their traditional food, see images of their landscape and architecture. Even if we can afford to buy Toyota Corolla, we still see a lot of BMWs and Mercedeses around us. We always see people who are more successful. They dress better, have better cars, have better houses, nicer jobs, eat fresher food, have more money. There are an endless number of professions. Our culture is also focused on &lt;strong&gt;fast&lt;/strong&gt;. Fast food, fast movies, fast reading. While on a plane to our home we already plan the next vacation. We read reviews instead of reading the book. We express ourselves in 280 characters because &lt;em&gt;ain’t nobody got time to read that essay I’m writing&lt;/em&gt;. And on top of all that, we are being told to look for that mysterious &lt;strong&gt;passion&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s too much to handle. Too much to choose from. So we try to have it all. We envy people who have big houses even if we don’t give a damn about big houses. We envy people who drive BMW even if we don’t care for driving. We are jealous of people who dress in brands even if we care only about minimalist, sustainable and simple clothing.&lt;/p&gt;
&lt;p&gt;Because that’s the world we live it. Our boredom from being pseudo-busy with our greed and desire for more, fuels this endless wheel that gives stage to even more greed and desire, and even more confusion about who we are and what we need to become. By putting attention to other people have, we only continue to fuel such society and continue to lose our self identity.&lt;/p&gt;
&lt;h2 id=&quot;you-dont-have-to-be-better&quot;&gt;You don’t have to be better&lt;/h2&gt;
&lt;p&gt;In fact - you even can’t be better. There is no better. Better is always relative. I can be better than you at software engineering because I’ve perfected that craft for years. It doesn’t mean I’m better than you. You might be a better writer, a better singer, dancer, photographer, storyteller. Whatever.&lt;/p&gt;
&lt;p&gt;We rip things we can compare such as material possession or amounts of money, and start to compare on that basis. But instead we need to focus on trying to understand what is really important to us. Do we really want that Lamborghini, or it’s just because we’ve been told that we want it?&lt;/p&gt;
&lt;p&gt;Advertising has done a tremendous job to convince us that we need that thing! And that, and this and these! We need them all. But life doesn’t have to be like this. By not knowing what you want from life, you end up wanting everything. Thus, always feeling miserable - because you can’t have everything. You can’t have everything - because deep inside you know, you know that you don’t need that Lamborghini, so you won’t blow up your savings to get one.&lt;/p&gt;
&lt;p&gt;So stop comparing yourself to other people. Focus on understanding your self and your values in life. What do you care about? You like gardening? Then maybe you need to focus on getting a nice house with a garden instead of feeling miserable when someone buys an apartment in a high rise building. You enjoy cycling? Then get a bicycle and cycle instead of being miserable when everyone around buys new cars.&lt;/p&gt;
&lt;p&gt;It’s that easy. The hardest part is just knowing what you want. So start focusing on that. Start finding the answer to the question: Who are you?&lt;/p&gt;</content:encoded></item><item><title>What Apple is doing right, and why I consider them to be minimalist</title><link>https://kudmitry.com/articles/what-apple-is-doing-right/</link><guid isPermaLink="true">https://kudmitry.com/articles/what-apple-is-doing-right/</guid><pubDate>Thu, 09 Sep 2021 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was always an Android fan.
I even remember resisting buying one of the Samsung Android phones that had a Home button similar to the old iPhones.
I remember saying in one of the forums: “&lt;em&gt;I’ll never touch anything that resembles an iPhones&lt;/em&gt;”.
Today I’m fully inside the Apple ecosystem.
Why?&lt;/p&gt;
&lt;h2 id=&quot;time-is-a-non-renewable-resource&quot;&gt;Time is a non-renewable resource&lt;/h2&gt;
&lt;p&gt;You probably read my &lt;a href=&quot;https://jikokaizen.blog/post/why-you-should-aim-for-10k-steps-a-day/&quot;&gt;10k steps a day&lt;/a&gt; post (if not — go read it!).
At that time I’ve had a Samsung Android phone and got a Xiaomi Mi Band for activity tracking.
After many years of resisting any smartwatches, I’ve started to see their appeal.
It’s nice to take a look at your wrist and know not only the time, but when’s your next meeting, how active you were today, and more — depending on your needs.&lt;/p&gt;
&lt;p&gt;So I decided to purchase a smartwatch.
I’ve put a lot of &lt;strong&gt;time&lt;/strong&gt; and effort into researching which one I want, which one suite me best.
Apple Watch was obviously out of the picture, even though it ranked as #1 in most reviews I’ve read, but hey — I’ve hated Apple (sort of, I was using a MacBook provided to me by my employer) and I’ve had an Android phone.
So I’ve settled on Galaxy Active 2 smartwatch.&lt;/p&gt;
&lt;p&gt;At first, it was great.
I was spending &lt;strong&gt;time&lt;/strong&gt; customizing it, putting different useful (and useless) widgets, browsing through Galaxy Store to acquire new Face Watches.
My watch was finally doing more than one thing.
It wasn’t as beautiful as traditional watch, but I’ve never owned any Omega or Rolex watches, and I’m more of a &lt;em&gt;function over form&lt;/em&gt; guy anyway.&lt;/p&gt;
&lt;p&gt;But then, problems started.
Even though I’ve had Samsung phone and Samsung watch, the integration between them wasn’t that great.
Notifications for events would arrive 2–3 hours after the event finished.
Most of the watch faces were pretty useless (who cares about moon phase?) and I’ve spent more &lt;strong&gt;time&lt;/strong&gt; looking for solutions in different forums and Reddit.
Moreover, Samsung’s ecosystem wasn’t that great, but their smartwatch insisted on using Samsung’s calendar or email client.
I’ve even started to follow Google Wear news to see when newer and better watch will come out.&lt;/p&gt;
&lt;p&gt;However, people around me — my friends, my wife, my colleagues at work — started to slowly switch to Apple and Apple Watch.
Based on my observation, end of 2020 and beginning of 2021, should have been the best time for Apple in terms of sales.
I’ve invested some more &lt;strong&gt;time&lt;/strong&gt; to understand more about Apple products, make sure they fit me, understand their shortcomings and strengths.
What can I say, I’m a very calculated guy.
And eventually, I’ve purchased an iPhone with an Apple Watch.
Sold my phone and galaxy watch, and happy since them.&lt;/p&gt;
&lt;p&gt;If you followed closely, you might have noticed that I’ve highlighted the word &lt;strong&gt;time&lt;/strong&gt; every time I had to spend it into something related to my phone or watch.
I’ve spent time for research, I’ve spent time customizing, and I’ve spent time troubleshooting.
Time is a non-renewable resource.
You can’t earn time.
Once its gone — its gone.
And instead of investing time in pointless customization or troubleshooting a poor product, I could have invested this time somewhere else — like socialization, side hustle or my self.
With Apple, things just work, and I rarely need to spend time to troubleshoot or customize my gadgets because there is very little to customize.&lt;/p&gt;
&lt;h2 id=&quot;the-paradox-of-choice&quot;&gt;The paradox of choice&lt;/h2&gt;
&lt;p&gt;One of the biggest complaints I’ve read about iPhone and Apple products in general — is the lack of customization.
&lt;em&gt;You can’t change the ringtone&lt;/em&gt;.
&lt;em&gt;There are minimal settings&lt;/em&gt;.
&lt;em&gt;Apple decides what’s best for you&lt;/em&gt;.
Those are all common complaints about Apple products.&lt;/p&gt;
&lt;p&gt;Today I believe that Apple simply following the minimalist philosophy — remove unneeded distractions.
There are &lt;strong&gt;zero&lt;/strong&gt; reasons to customize your wallpaper or change your ringtone.
You feel good the first few minutes, but then — poofff — this feeling is gone.
You no longer care about your ringtone or wallpaper, and if you do, you are probably focusing your energy on the least important thing in your life.&lt;/p&gt;
&lt;p&gt;In his book, &lt;a href=&quot;https://www.amazon.com/The-Paradox-of-Choice-audiobook/dp/B00429IOPI/ref=sr_1_1?crid=1HP08CIO20TXV&amp;#x26;dchild=1&amp;#x26;keywords=paradox+of+choice&amp;#x26;qid=1631042732&amp;#x26;sprefix=paradox+of+choice%2Caps%2C457&amp;#x26;sr=8-1&quot;&gt;The Paradox of Choice: Why More is Less&lt;/a&gt;, Barry Schwartz talks about how abundance of choice actually is bad for us.
It makes us paralyzed and unable to make a decision.
When you have millions of settings in your phone, you tend to avoid touching them because it’s too hard to find the right balance.
So you are left with a non optimized device.
While I don’t think that Apple got it right for 100% of their customers, I do believe that they got it right for the majority.
And those who are left out — have a choice to get a more suitable device from other manufacturers.&lt;/p&gt;
&lt;p&gt;On the same note, when I &lt;a href=&quot;https://jikokaizen.blog/post/3-things-ive-learned-by-breaking-my-phone/&quot;&gt;broke my old phone&lt;/a&gt;, I had to buy a new one.
There were so many options.
Different camera, different screen size, different manufacturers (each with their own customization).
Being who I am — means I have to invest time to do my best research.
I like to maximize the value I get for my money.
However, once my iPhone will die, I know what will be my next phone — iPhone.
I just have to choose the one the fits in my budget.
I don’t need to spend time by watching reviews or scouting forums for suggestions.
By purposely limiting myself, I’ve eliminated and unneeded future decision.
Minimalism at its beauty — eliminate unneeded distractions.
Phones are tools.
At least for me.
And a tool should work and serve me, my time and my needs.
When I buy a car — I don’t put stickers on it and waste time painting the wheels in different colors.
Nor I do it with my fridge or my TV.
Why do I do that with my phone then?
Yes, you can argue that it’s a form of self-expression, but I’ve never heard of someone being judged by the wallpapers on his or her phone.
There are others ways to express your self.
It can be through creation — like writing this blog is for me.&lt;/p&gt;
&lt;h2 id=&quot;something-for-everyone&quot;&gt;Something for everyone&lt;/h2&gt;
&lt;p&gt;Everybody is different, obviously.
People have different needs.
I’m a believer in free market — demand equals supply.
If a product is available on the market — there are customers who buy it.&lt;/p&gt;
&lt;p&gt;However, I’ve realized that by giving people too many options — it either confuses them like Barry Schwartz wrote in his book; or makes them curious - so they start to spend time finding the best wallpaper that works with their ringtone and matches their icon set.
I’ve been there.
But it’s time to move on and focus on things that fulfill me, rather than on customizing my gadgets.
And while Apple is not perfect — there are annoyances, in the end they are targeting a 7 billion market of potential, &lt;strong&gt;unique&lt;/strong&gt;, customers — its obvious their products won’t fit everyone 100%.
For those — there are other products and solutions.&lt;/p&gt;
&lt;p&gt;But I believe more companies should adopt this mindset.
Mindset of removing distractions.
Mindset of simplifying decisions.
I’m currently working on a product and I try to incorporate those mindsets in it.
And it’s sad to see that many companies actually take step backwards and introduce as much customization as possible, so much that it becomes scary or frustrating to use their products.
I’ve seen people, in product management mainly, struggle to create the best &lt;a href=&quot;https://www.atlassian.com/software/jira&quot;&gt;Jira&lt;/a&gt; board because its so customizable it’s paralyzing.
And jumping between two different Jira boards on the same organization — is like learning a whole new profession because no two Jira boards are alike.&lt;/p&gt;
&lt;p&gt;People tend to think they want customization.
The more — the better.
But in reality, too much customization can paralyze.
Adopt minimalist lifestyle in your digital world as well.
You don’t have to move to Apple if you don’t want to or can’t.
I’m not saying it is the holy grail for everyone.
But at least try to remember that those are tools there were created to help you become better in life — more organized, more calm, more fulfilled, more productive.
Stop wasting time by serving your gadgets and instead let them serve you.&lt;/p&gt;</content:encoded></item><item><title>Things I wish I knew when I started with Minimalism</title><link>https://kudmitry.com/articles/things-i-wish-i-knew-when-i-started-with-minimalism/</link><guid isPermaLink="true">https://kudmitry.com/articles/things-i-wish-i-knew-when-i-started-with-minimalism/</guid><pubDate>Mon, 30 Aug 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I don’t remember when or why I started with minimalism.
Knowing myself I’ve probably read about it somewhere, then watched a video, read some more, and more, and more — and eventually my mind was changed, and I was like “Ok, let’s try it”.&lt;/p&gt;
&lt;p&gt;As with any new habit, I’ve got hyped, excited and started to declutter my life from items that no longer “spark joy” (c).
I’ve created declutter rituals and even introduced a new habit I’ve called “Monthly Declutter” which happens on last Saturday of every month.
During this declutter session I need to get rid of at least one item I no longer use / need / sparks joy.
Its also worth mentioning that I try not to throw items away, but instead have a sustainable declutter checklist which you can read about &lt;a class=&quot;internal new&quot; href=&quot;/articles/minimalism-and-sustainability/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But now, few years later, I wish someone told me the following things before I started my declutter journey towards minimalist life.&lt;/p&gt;
&lt;h2 id=&quot;it-can-be-a-form-of-escapism-and-its-not-healthy&quot;&gt;It can be a form of escapism and it’s not healthy&lt;/h2&gt;
&lt;p&gt;When we are vulnerable, hurt, depressed or just sad — we tend to engage in (semi) self-destructing behaviors.
We overeat sweets, over watch TV, overplay computer games.
I’m not a doctor, but my guessing is that when we are sad, we want to escape this mood.
And engaging in one of the aforementioned activities helps us escape reality.
Sugar provides chemicals (like cocaine) such as endorphins and dopamine which are linked with feeling happy, and TV and computer games are literally an escape to a different world with an easy “achievement scale”;
When watching TV we are progressing together with the main character;
And when playing games we are leveling up pretty fast compared to progress in real life.
Sometimes however, when people are sad or angry they do sport.
They lift aggressively or punch a punching bag.&lt;/p&gt;
&lt;p&gt;I, on the other hand, chose to escape by decluttering.
When you label your self as minimalist and strive to get rid of items you no longer need, it sparks a sense of achievement in you when you get rid of something.
Sense of achievement releases the same chemicals as sugar consumption, so you start to feel better about yourself.
But instead of dealing with the problem of why I am sad, or just letting myself be sad, I decluttered for the sake of decluttering.
In my opinion, it’s a form of the same harmful escapism as overeating or over watching TV.&lt;/p&gt;
&lt;h2 id=&quot;it-always-needs-to-be-in-context&quot;&gt;It always needs to be in context&lt;/h2&gt;
&lt;p&gt;In combination with the above point, I’ve had this itch in the back of my head to declutter my winter cloth.
I try to follow a rule of “&lt;em&gt;if I haven’t worn this item for 1 year, and it’s not a specialty item (like a ski coat) — get rid of it&lt;/em&gt;”.
And I haven’t used most of my winter items for the past year.
So every time I grab my cloth with the intention to declutter, I suddenly realize that 2020 and 2021 are no ordinary years.
I’ve spent the majority of the winter in lock down hence I haven’t had the chance to wear most of my winter cloth.&lt;/p&gt;
&lt;p&gt;Everything, including minimalism, should be evaluated in context.
Because only when we put things in context, we can understand the true purpose of our actions.&lt;/p&gt;
&lt;h2 id=&quot;minimalism-is-not-about-decluttering&quot;&gt;Minimalism is not about decluttering&lt;/h2&gt;
&lt;p&gt;When you get into minimalism, the most common type of questions you hear in different communities are: “&lt;em&gt;Should I keep [this item]?&lt;/em&gt;”, “&lt;em&gt;Can I be a minimalist if I have [that item]?&lt;/em&gt;”, “&lt;em&gt;I don’t use [these items], should I get rid of them?&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;Obviously, by reading different articles or watching different videos, I got the same message — minimalism is about getting rid of what you don’t need.
Tidy up your home.
Get rid of the TV because there are movie theaters.
Be able to pack your entire apartment in 30 minutes, should you need to move.
Have only double-digit number of items.
Live from a backpack.&lt;/p&gt;
&lt;p&gt;And today, few years after I’ve discovered minimalism, I can say that there are items I regret getting rid of.
Not because I have emotional attachment to them, but because I want to use them again.&lt;/p&gt;
&lt;p&gt;I’ve sold 2 bicycles because I thought I’ll never ride them again, and wanted to tidy up my apartment.
Eventually I had to re-buy one of them.
I’ve re-bought a tripod that I’ve previously sold.
I almost sold my hiking equipment which I now use again.&lt;/p&gt;
&lt;p&gt;And I think this is the most important lesson I’ve learned that I wish I knew when I started to practice minimalism:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Minimalism is not about getting rid of items.
It’s mainly about being intentional with what you buy, evaluating what you have, and only as last resort getting rid of what you no longer need.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It’s easy to get rid of items.
As I’ve mentioned previously it can even be a source of fast endorphin and dopamine release.
But this is not what minimalism is about.
Minimalist is just a tool (or philosophy if you want to call it that), that helps us navigate in the consumerist world we live in.
Minimalism is a philosophy that urges you to &lt;strong&gt;not&lt;/strong&gt; tying up your life, self-worth and respect to material possessions.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;You are not your job, you’re not how much money you have in the bank.
You are not the car you drive.
You’re not the contents of your wallet.
You are not your fucking khakis.
You are all singing, all dancing crap of the world. - Tyler Durden, Fight Club&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;By getting rid of items you no longer need and re-buying them later, you continue to spin the consumerist hamster wheel.
Moreover, if you get joy from decluttering, you might even subconsciously buy more just to have an excuse to get rid of older items.&lt;/p&gt;
&lt;p&gt;I wish that when I’ve started practicing minimalism, someone would tell me that.
I wish that instead of worrying how I reduce my possessions to fit in a backpack, I’d focus more on how to buy intentionally, how to find my passions and hobbies.
I wish that I’d remember that I evolve as a person, and my evolution means I can change hobbies, which means I’d have to reacquire equipment for my new hobbies, equipment that I might have had and sold.
But instead, &lt;a href=&quot;https://www.reddit.com/r/minimalism/&quot;&gt;/r/minimalism&lt;/a&gt; is filled with decluttering content, and only rarely someone talks about being intentional with what you buy.&lt;/p&gt;
&lt;p&gt;Now, don’t get me wrong.
I’m not saying that you need to keep your bicycle.
If you’ve tried mountain bicycling, and you didn’t like it, then it’s safe to sell your bicycle.
But before selling it, ask your self the following set of questions:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;By selling my bicycle, am I escaping a deeper issue?
Am I sad now?
Maybe there is a fucking pandemic outside, and I’m trying to escape reality by selling my mountain bicycle (which I can’t use because I can’t leave my home)?
Or am I trying to reach some mystical goal of having a certain number of possessions?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Worst case, if you want to try again some time in the future, just buy it again.
However, it’s also worth asking your self the following set of questions prior to your re-purchase:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Why do I want to try mountain cycling again?
I’ve tried it once and didn’t like, what changed now?
Am I under the influence of advertisement or social media that mountain cycling is cool or have I &lt;strong&gt;changed&lt;/strong&gt; as a person and wish to try this kind of sport again?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mistakes were made.
I’m not in deep grief.
Those are items in the end.
Yes, I’ve lost money.
But this is money I’ve paid for a lesson.
A very valuable lesson.
You can’t live life without making mistakes.
So that’s fine.
But I have this feeling that a lot of people they focus too much on the numbers.&lt;/p&gt;
&lt;p&gt;Numbers are easy to compare.
I make X$ per hour, you make Y$, so one of us is better.
You have X items, I have Y — so one of us is practicing minimalism better.
It’s way harder to compare ourselves on an arbitrary “intentional buyer” scale.
It’s very easy, on the other hand, to compare ourselves on “possessions count” scale.
But there is no need to put numbers on everything or try to measure everything.
There is no &lt;em&gt;better&lt;/em&gt; minimalism.
There is minimalism — which is just an idea to be intentional with the items you buy and use.&lt;/p&gt;</content:encoded></item><item><title>Internet is killing your brain</title><link>https://kudmitry.com/articles/internet-is-killing-your-brain/</link><guid isPermaLink="true">https://kudmitry.com/articles/internet-is-killing-your-brain/</guid><pubDate>Wed, 25 Aug 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;People who smoke usually know that smoking might prematurely kill them. Or if not kill, then give them cancer, stroke, heart disease, lung disease etc. People who drink alcohol are aware that they will have liver problems and might prematurely die. Eating processed food leads to obesity which in turns might lead to cardiovascular disease. It’s all known, It’s hard to argue with it, the data is there (look at sources in NHS, WHO, CDC). But very few people know that internet damages your brain.&lt;/p&gt;
&lt;h2 id=&quot;internet-is-the-vegetable-diet-among-fast-food&quot;&gt;Internet is the vegetable diet among fast food&lt;/h2&gt;
&lt;p&gt;If someone asked me, what was the best invention in the past 100 years, I’d say - the Internet. I have experience life with a PC and no internet. I have experiences dial-up internet when picture took minutes to load and going on the internet required approval from family members - because they won’t be able to use the phone. I’ve experienced ADSL speeds. And now experiencing fiber optic speeds as well as being connected 24/7 thanks to my smartphone.&lt;/p&gt;
&lt;p&gt;Without internet, devices like Kindle and Smartphones would be useless, services like Spotify, Audible, YouTube and this blog - would not exist. Internet gave us instant access to shopping. Internet gave us unlimited access to knowledge. Internet gave us ability to stay connected with our loved ones, especially now during pandemic. Internet gave us international collaboration. But every coin has 2 sides.&lt;/p&gt;
&lt;h2 id=&quot;internet-is-also-the-cocaine-among-drugs&quot;&gt;Internet is also the cocaine among drugs&lt;/h2&gt;
&lt;p&gt;Internet addiction is not a new term. Our society is discovering new anxiety disorders like Fear Of Missing Out. We’ve created terms like Internet Bullying to describe online bullying. People are being shamed. Deceived. Raped and killed. All via, or with the help of, the internet. And while undoubtedly those issues must be addressed, there is another issue that nobody is talking about - and it’s the pandemic of our times. The pandemic of the false information, the pandemic of fear spreading.&lt;/p&gt;
&lt;p&gt;With access to entire human competence and knowledge, we also got access to entire human stupidity, opinions and beliefs. And if in pre internet times, the weirdo who thinks that the government spies on him, would put a tin foil cap and move to live in the woods - today he expresses his opinion on YouTube, Facebook, Twitter and other platforms.&lt;/p&gt;
&lt;h2 id=&quot;all-the-cars-in-the-world-are-red&quot;&gt;All the cars in the world are red&lt;/h2&gt;
&lt;p&gt;Imagine a situation where all you see are red cars. You walk on the street and all the cars are red. Those that are parked. Those that are being driven. Taxis. Buses. All red. Would you believe me if I told you that I’ve seen a blue one? You’d probably be skeptical.&lt;/p&gt;
&lt;p&gt;We tend to form our opinions based on the world we see around us. We see green apples, and we think that apples can only be green. But then we travel, we discover new kinds of apples. We discover new people, new traditions and new religions. And suddenly we understood more about the world. We’ve seen that there are cars other than red.&lt;/p&gt;
&lt;p&gt;The internet works similarly. You watch YouTube video after YouTube video until you find yourself sitting in front of your computer at 3 AM looking at cats or worse - conspiracy theories how aliens manipulate humans.&lt;/p&gt;
&lt;p&gt;Your engagement time with different services (almost) always have direct conversion to revenue. Then more videos you watch, the more ads you see, the more revenue the service makes. And in order to maintain this cause and effect, online services have a recommendation mechanism in place. The way it works is pretty simple. You watch a video about cats, the algorithm assumes you like cats, so it recommends you another cat video. If you watch it, its assumption becomes stronger, and you will see more cat videos.&lt;/p&gt;
&lt;h2 id=&quot;show-me-your-youtube-homepage-and-ill-tell-you-who-you-are&quot;&gt;Show me your YouTube homepage and I’ll tell you who you are&lt;/h2&gt;
&lt;p&gt;There is a saying by an Unknown that goes like this: &lt;em&gt;Show me who your friends are, and I’ll tell you who you are&lt;/em&gt;. The idea behind it, and I believe in it, is that people tend to be attracted to similar minds. Successful people rarely hang out with bums. Athletic people rarely hang out with physically inactive people. They might, but its temporary, and eventually they will drop them as friends or drag them to their level (it works both ways - a shy nice kid in a company of schools hooligans will either become bored and looks for other shy nice kids to befriend or be dragged down and become a school hooligan).&lt;/p&gt;
&lt;p&gt;The same can be said about your social feeds. What you see there is an exact reflection of who are you and what you believe in. Now there is a very big question that I do not know the answer for yet - which came first? Your beliefs that guided you towards specific YouTube videos or those videos that shaped your beliefs? I tend to speculate that it’s a bit of each.&lt;/p&gt;
&lt;h2 id=&quot;deep-down-the-rabbit-hole&quot;&gt;Deep down the rabbit hole&lt;/h2&gt;
&lt;p&gt;The problem is that if you are not very, very, &lt;strong&gt;very&lt;/strong&gt; careful - you might end up deep in a rabbit hole. Video after video, post after post it will drag you down, narrowing your world view, slowly shaping your beliefs. And exiting this rabbit hole is hard. It requires having open mind, it requires being &lt;strong&gt;active&lt;/strong&gt;, way beyond the level of activity needed to click on the next video or scroll to the next post. You actively need to challenge your world views. Challenge your beliefs. Expose your self to different sources of information. Because if you keep staying in the city of red cars, you will never discover that cars can be made in different colors.&lt;/p&gt;
&lt;p&gt;I always like to end up with a personal touch. Advice from myself. I don’t believe I’m qualified to discuss a topic I don’t have experience in.&lt;/p&gt;
&lt;p&gt;But honestly, giving an advice here is very hard. Because the best advice I can give you is that you need to train your mind. Train it to challenge information. Remember than news never speak about good things, they only talk about death and murder, war and disease. It doesn’t mean that those are the only things outside the window. Its just that news are not a good source of positive and educational information. Look elsewhere. The same with social networks. If you allow the algorithm to decide what you like - you might end up in a rabbit hole. It doesn’t mean you shouldn’t click on controversial videos, or share controversial posts. It means that by doing so, you also need to take control of what information you want to be exposed to. And you need to take control and verify your information.&lt;/p&gt;
&lt;p&gt;YouTube (and probably other social networks) have a way to control how the algorithm works. Each recommended video you see can be tagged as “Not Interested” or “Don’t recommend channel”. By doing so, you can control how the algorithm works without dragging you deep into the rabbit hole.&lt;/p&gt;
&lt;p&gt;My YouTube homepage looks the same. Exactly same 10–20 videos are being offered to me with each refresh. No matter what kind of videos I watch, even when I watch controversial, fake, provoking videos or click on videos out of boredom or when procrastinating - once I start getting recommendations to them, I flag them as either “Not interested” or “Don’t recommend channel”. This way &lt;strong&gt;I&lt;/strong&gt; control what information I see. Not the algorithm (whose purpose is to offer you more cat videos if you like cats - which by itself is not a bad thing, but remember every coin, two sides..)&lt;/p&gt;
&lt;p&gt;Take control. If you start to feel that your feed is taking control of you, you feel anxiety, stress, panic - STOP. Evaluate whether the information you see is relevant and doing good to you. If not, quit or block. I’ve had a nervous breakdown recently from reading too much news. I’ve blocked them. Didn’t miss anything. Its still pandemic outside.&lt;/p&gt;
&lt;p&gt;When I’ve noticed that YouTube is taking me down the rabbit hole I’ve stopped. Reevaluated what videos I’m being offered, removed the one that are bad for me. Found a hobby (like reviving this blog).&lt;/p&gt;
&lt;p&gt;You also need to consider how you are getting your information. There are 2 mains methods: Push method and Pull method.&lt;/p&gt;
&lt;p&gt;In a push method you are being offered information. You see YouTube home page with video recommendations, Facebook news feed with post recommendations. In push method you rarely have control. Push method is the way to the rabbit hole. You are bored or have a minute to spare, so you open your feed, see some information, click on it which makes the algorithm think you are interested in it so you are being offered more of it. At some point you end up in the rabbit hole I’ve told you about. Sure, some of the best YouTube videos I’ve watched were offered to me by the recommendation algorithm, but at that point I’ve already had control over what information I want to be exposed to. I knew how the push method worked and could control it. If you lack this control, skip the push method and start with pull.&lt;/p&gt;
&lt;p&gt;Pull method is different. In pull method you search for information. You want to discover something new so you go and search for it. You watch videos, you read posts. You form an opinion. It’s different from push in one specific aspect - &lt;strong&gt;control&lt;/strong&gt;. In pull method you have control, in push method - you usually don’t (unless you already have enough self-control to stop, criticize, challenge and evaluate). Pull method is active, while push is passive. You are less likely to engage in pull method when you are bored or procrastinating hence your ability to criticize and challenge information is higher at those moments. Your mindset is different. In pull method you are set to learn or find the truth. In push method you are set to relax or kill the boredom. You lack control.&lt;/p&gt;
&lt;p&gt;Remember this the next time you scroll through your feed.&lt;/p&gt;</content:encoded></item><item><title>Minimalism and Sustainability</title><link>https://kudmitry.com/articles/minimalism-and-sustainability/</link><guid isPermaLink="true">https://kudmitry.com/articles/minimalism-and-sustainability/</guid><pubDate>Mon, 02 Aug 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When you get interested in Minimalism, one of the first question you ask your self is: “&lt;em&gt;What do I do with the stuff that I don’t need?&lt;/em&gt;”.
Well, the obvious answer might be — throw it away.
But sometimes, the things that I don’t need are things that still work or function properly.
And throwing away a working item, makes me… sad.
So I came up with the following 5-step system to help me decide what to do with an item that no longer “sparks joy” (c).&lt;/p&gt;
&lt;h2 id=&quot;1-is-it-useable-and-sellable-without-too-much-effort&quot;&gt;1. Is it useable AND sellable without too much effort?&lt;/h2&gt;
&lt;p&gt;The first step in my 5-step evaluation system gives the maximum benefit to me.
I can get rid of an item I no longer need and get some of its monetary value back.
For every item I no longer need or which I’ve replaced with better version of it, I ask this question: &lt;em&gt;Is it useable / in a working condition, and can I sell it without too much effort?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the item is broken, I move to the next step in this list.&lt;/p&gt;
&lt;p&gt;If selling it requires too much effort: for example a rare item that no one needs; a piece of cloth that is practically impossible to sell (however I’ve managed to sell an unworn cardigan once); the ratio between the money I can make versus the time I’ll invest in answering messages / phone calls, meeting with people and price negotiation is too low? — I move to the next step in this list.&lt;/p&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;tip&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M15 14c.2-1 .7-1.7 1.5-2.5c1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5c.7.7 1.3 1.5 1.5 2.5m0 4h6m-5 4h4&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;TIP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;Use your local selling groups on Facebook.
You can also try Facebook Marketplace and dedicated websites for second hand items (I know that in the U.S. they have Craigslist and eBay).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;2-can-it-be-gifted--donated--given-away-without-too-much-effort&quot;&gt;2. Can it be gifted / donated / given away without too much effort?&lt;/h2&gt;
&lt;p&gt;If I’m not able to sell the item, I look for giving it away either to friends or family, or strangers who were looking for such item.
Some friend may need wine glasses that I no longer need.
An organization that works with children who have disabilities might need an old PC I no longer use.
Of course not all items are in a condition to be given away.
Sometimes there are no friends in need of my items or there is no charity that willing to accept old items or finding such charity takes too much time (for example driving to a different city) — then I go to the next bullet.&lt;/p&gt;
&lt;p&gt;A side note for this — some items are better given away in bulk.
The other day I was decluttering, and I collected a bunch of PC cables, USB thumb drives and some other peripherals.
Selling them according to bullet 1 is too cumbersome — I’m not sure if they work, nobody is going to buy a second hand PC cable and the amount of time I’d invest in selling them compared to the income I can get, is not worth it.
I could just recycle them or throw them away, but instead I found a local Makers community, posted on their Facebook that I have a bunch of stuff that I don’t need, I don’t know in want condition it is, and I’m willing to give it to the first person who will come and take &lt;strong&gt;all of it&lt;/strong&gt;.
Within 2–3 hours, I gave everything away.&lt;/p&gt;
&lt;h2 id=&quot;3-is-it-working--useable--not-broken-and-can-be-left-on-the-street&quot;&gt;3. Is it working / useable / not broken AND can be left on the street?&lt;/h2&gt;
&lt;div class=&quot;callout&quot; data-callout=&quot;note&quot; data-collapsible=&quot;false&quot;&gt;&lt;div class=&quot;callout-title&quot;&gt;&lt;div class=&quot;callout-title-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;g&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;10&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M12 16v-4m0-4h.01&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;callout-title-text&quot;&gt;NOTE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;callout-content&quot;&gt;&lt;p&gt;I’m not sure what are the laws in each country.
The country I live in, it’s pretty common to leave old stuff on the street, so somebody will take it.
Check your local laws.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Sometimes, some items are perfectly useable, but it’s hard to give them away.
Shoes for example.
In such cases I try to leave such items on the street, where they can be seen.
Strangers can bypass and take them.
Usually it happens on the same day.
If not, I’ll pick this item up the next day and proceed to step 4 in this list.&lt;/p&gt;
&lt;h2 id=&quot;4-can-it-be-recycled-without-too-much-hassle&quot;&gt;4. Can it be recycled without too much hassle?&lt;/h2&gt;
&lt;p&gt;Once again, this depends on your local laws, but in my country there are recycling facilities for textile, electronics, glass, etc.
I always opt out for recycling before throwing away.
However, if it takes too much time, I’d most likely just throw it away.&lt;/p&gt;
&lt;p&gt;The other day I wanted to recycle an old PC mouse.
I went to my local recycling center but due to COVID they were closed.
I tried another one, but due to bureaucracy they won’t let me inside the building and the guard refused to take the mouse for me.
This is too much hassle.
I’ve already invested time in going to those recycling facilities, and with a great feeling of sadness I had to just throw it away.&lt;/p&gt;
&lt;h2 id=&quot;5-throw-it-away&quot;&gt;5. Throw it away&lt;/h2&gt;
&lt;p&gt;Nothing to add.
If you can’t sell it, give it away or recycle it — then throw it away.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Transitioning into a more minimalist lifestyle, or even just replacing items I no longer like / need / use with better versions — always raise the question, for me, as what to do with the old item.
I try to leave a positive print on the environment and defaulting to throwing away old items felt wrong for me.
Getting some money back or making someone happier by giving them items I no longer use, felt like a good middle ground for me.
I hope this 5-step evaluation system will help you as well.&lt;/p&gt;</content:encoded></item><item><title>Why you should aim for 10K steps a day</title><link>https://kudmitry.com/articles/why-you-should-aim-for-10k-steps-a-day/</link><guid isPermaLink="true">https://kudmitry.com/articles/why-you-should-aim-for-10k-steps-a-day/</guid><pubDate>Sat, 24 Apr 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Somewhere around the beginning of February, I’ve found my old Mi Band 2 fitness tracker. I’ve stopped using it because the bracelet broke, I’ve ordered a new one on eBay, but it never came, so I threw the Mi Band in a drawer and forgot about it.&lt;/p&gt;
&lt;p&gt;But then I found it, decided to buy a new bracelet for it, realized that Xiaomi already released Mi Band 5 (and there is Mi Band 6 already) which is very cheap, so I decided to get it. The reason I wanted a fitness tracker was mainly for tracking my sleep and use it as a discreet alarm that won’t wake up my wife. So I got it, started to wear it and play with it and then asked my self “What if I’ll try to do 10K steps a day?”.&lt;/p&gt;
&lt;h2 id=&quot;why-10k-a-day&quot;&gt;Why 10K a day?&lt;/h2&gt;
&lt;p&gt;There is no particular reason why you should aim to do 10K, but from the research I did, the number 10,000 mainly became popular in 1965 because a Japanese company released a device named “Manpo-Kei” which literally translates as “10,000 step meter” (Source: &lt;a href=&quot;https://en.wikipedia.org/wiki/Pedometer#History&quot;&gt;Wikipedia&lt;/a&gt;) and the man (Y.Hatano) behind it claimed that “10,000 steps a day is ideal”.&lt;/p&gt;
&lt;p&gt;There are many articles on the internet as to why 10K steps a day is healthy, however no major health organization actually says that 10K steps a day is ideal (at least I could not find any reference to it) and their recommendation is based on activity intensity and time (i.e. at least 150 minutes of moderate activity a week - source &lt;a href=&quot;https://www.who.int/news-room/fact-sheets/detail/physical-activity&quot;&gt;WHO&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;No pedometer is accurate, but according to my average, 10K steps is somewhere around 7.5 km (approx 4.6 miles) and amounts a total of approximately 1 hour and 30 minutes of walking.&lt;/p&gt;
&lt;h2 id=&quot;how-to-reach-10k-steps-a-day&quot;&gt;How to reach 10k steps a day?&lt;/h2&gt;
&lt;p&gt;“&lt;em&gt;Jeez, 7.5km and 1.5 hours a day - &lt;strong&gt;is a lot&lt;/strong&gt;!&lt;/em&gt;” - you might say. And yes, it is. And chances are that you are like me - stuck for the past year at home, both living and working from it, then you probably do around 2k-3k steps a day. I know I did when I got Mi Band 5 and started to count my steps.&lt;/p&gt;
&lt;p&gt;I remember when the first lock-down became less strict, and we were allowed to travel up to 1km from home (as opposed to 100m), I remember that walking or even climbing 2–3 stairs (not 2–3 &lt;strong&gt;flights&lt;/strong&gt; of stairs, but 2–3 stairs!) was exhausting!&lt;/p&gt;
&lt;p&gt;So armed with the knowledge that 10K steps is better than nothing, I’ve started slowly to build my habit towards 10K steps a day. At first, I did 5k for a week, then 6k, then 7k, then 9k when eventually I was able to reach 10k.&lt;/p&gt;
&lt;p&gt;If you are just starting, I suggest you do the same. Don’t start with 10K, start smaller and be consistent. Remember - it’s better to do less but consistently, than to aim for more but not doing it at all.&lt;/p&gt;
&lt;p&gt;I never really reached 10k consistently. Some days I do 5k, some days 20k. My best streak was 5 days in a row. But on some days I ride my bicycle for 1 hour, and some days I punch the boxing bag for 1 hour. I don’t see a point in doing 10k steps on those days - as my primary objective in doing 10k steps was to &lt;strong&gt;stay active&lt;/strong&gt; every day, and on days I’m already active because I either cycle or box, I don’t need to reach my 10k steps a day.&lt;/p&gt;
&lt;p&gt;However by doing 10k steps a day, I’ve found some interesting benefits I want to share with you and maybe motivate you to &lt;strong&gt;try&lt;/strong&gt; to reach 10k steps a day.&lt;/p&gt;
&lt;h2 id=&quot;benefits-of-doing-10k-steps-a-day&quot;&gt;Benefits of doing 10k steps a day&lt;/h2&gt;
&lt;h3 id=&quot;its-easy-to-start&quot;&gt;It’s easy to start&lt;/h3&gt;
&lt;p&gt;There are two modes to the human body - active and resting. Resting is when you sit in your chair or lay on your sofa. Active is when you move. And when you move, your heart rate goes up, blood is circulating your body, your legs and arms are moving, muscles are working to keep you standing and in balance.&lt;/p&gt;
&lt;p&gt;Leading a resting/sedentary lifestyle can lead to, you guessed right - heart disease, back problems, cancer and premature death. And if you know me and read my previous posts, I’m all about becoming super healthy, super human and living as much pain free and long life as I can.&lt;/p&gt;
&lt;p&gt;But the problem is that getting into any sport - requires financial investment and developing a mental love to it. Even simple things like running - requires you at least get comfortable running shoes, and I won’t talk about other sports like gym - which requires membership or equipment if you want to work out from home; cycling - which requires a bicycle - obviously; boxing, rowing, skiing - which all require equipment / membership and the latter also depends on weather. More over, sport is an individual thing. I hate running. I tried, multiple times. I hate it. Some people swear by it, I suffer when I run. And I don’t like to suffer, so I don’t run.&lt;/p&gt;
&lt;p&gt;However, walking is something native to us. We walk all the time - whether its from the chair to the fridge, from our houses to a bus stop, in a supermarket, or just by the beach. Walking is natural to human beings. It requires zero equipment (you can even walk barefoot - some people claim it’s better, I prefer shoes though) and it’s practically impossible to hate it - as it’s the basic form of moving as opposed to sitting.&lt;/p&gt;
&lt;h3 id=&quot;its-great-for-multitasking&quot;&gt;It’s great for multitasking&lt;/h3&gt;
&lt;p&gt;When you run or cycle, row or lift weights - you are most likely trying to reach a goal be it distance, number of repetitions, weight of each repetition or speed - but you try to push your body to its limits. And when you push your body to its limits, it is hard to do other tasks such as catching up with family or friends. Try to run and have a phone call with your friend or do bench press and catch up with your family.&lt;/p&gt;
&lt;p&gt;Walking on the other hand does not raise your heart rate too much (unless you are doing fast walking) which enables you to do other tasks. When I walk I tend to use this time to: catch with family or friends, listen to audiobook or podcast, listen to music, or clean my mind and explore, which brings me to the next 2 points.&lt;/p&gt;
&lt;h3 id=&quot;its-good-for-cleaning-up-your-mind&quot;&gt;It’s good for cleaning up your mind&lt;/h3&gt;
&lt;p&gt;Whether you’ve had an argument with your SO, can’t figure out a solution to hard problem or your mind is just occupied with thoughts, it’s always a good decision to take a walk.&lt;/p&gt;
&lt;p&gt;By walking, you are not worried about speed or distance, repetitions or weight, you just move - leaving your mind free to focus on other things. When I’m worried or have troubles concentrating, I’m going for a walk. It helps me clean my mind, get rid of my worries. There is even a walking meditation if you’d like to try it (I haven’t). I usually feel way better in terms of concentration or worries - when I come back from a walk.&lt;/p&gt;
&lt;h3 id=&quot;it-brings-the-explorer-in-you-and-teach-you-to-appreciate-whats-around-you&quot;&gt;It brings the explorer in you and teach you to appreciate what’s around you&lt;/h3&gt;
&lt;p&gt;Human beings are explorers. Before creating a settlement, we used to wander in searches for the best place to settle. Close to water source, away from predators, uphill for easier defense. By walking daily - you feed the explorer in you.&lt;/p&gt;
&lt;p&gt;You might have moved to a new neighborhood or live in the same one for the majority of your life - but how much of it you really know? Can you navigate your way home from any point in your neighborhood without a GPS? Do you know to tell me where I can find a good park or a nice bakery in your neighborhood, without checking Google Maps? I’ve learned to appreciate this exploration, I found some nice restaurants, great parks that are few minutes of walk away from my home, and it comes really helpful now, when my wife and I are looking to move to a different apartment. Before I’ve used to walk and explore my neighborhood or the ones close to it, I wouldn’t know who lives in each neighborhood. Is it a good neighborhood? Is this green spot on Google Maps a park or just some grass with benches? But after exploring my neighborhood and the ones close to it, I know to say where good parks are located. What type of people live in each neighborhood. Whether it’s a noisy one because it’s located near a central street or a school or a quiet one because its full of small houses rather than tall buildings.&lt;/p&gt;
&lt;p&gt;It also helped me to learn to appreciate things. When you walk, your mind is free to look around, judge the architecture, explore parks, smell the smell of fresh bread or coffee. The internet is full of negative stuff, and by taking a walk I balance the negativity with positive appreciation. There was one day when I was walking and a light rain started, but instead of panicking and hiding I’ve just embraced it and walked under the rain, smiling.&lt;/p&gt;
&lt;h3 id=&quot;it-gives-you-perspective-on-distance&quot;&gt;It gives you perspective on distance&lt;/h3&gt;
&lt;p&gt;Before I started to aim to do 10k steps a day, I’d look at directions to my doctor’s appointment, restaurant or a shop, in Google Maps and judge its distance. 30 minutes of walk, to me back then, considered too long. I would opt for a bus ride or a taxi in that way, even if it took the same 30 minutes. My mind just wasn’t grasping 3-4km of walking as something bearable or worth doing. But after I’ve walked 10k steps for some time now and realized that it takes around 1.5 to 2 hours and amounts to around 7-8km - I look at directions differently.&lt;/p&gt;
&lt;p&gt;The other day, I was invited to a gathering activity with my work colleagues. The walk was 45 minutes and 3.5km from the place I live. 5–6 months ago I would have never taken that walk, if you’d asked me to. For me at that time, 45 minutes of walking is too much. Today I realize that walking back and forth is not even 10k steps. And by aiming to do 10k steps a day, I decided to take that walk. And was engaged in other activities such as catching up with family, listening to an audiobook, exploring new locations and appreciating the beautiful world we live in.&lt;/p&gt;
&lt;h2 id=&quot;how-to-start&quot;&gt;How to start?&lt;/h2&gt;
&lt;p&gt;I want to finish with some tips on how you can start building your habit of walking 10k steps a day. Remember 2 important principles:&lt;/p&gt;
&lt;h4 id=&quot;1-you-dont-need-any-fancy-equipment&quot;&gt;1. You don’t need any fancy equipment&lt;/h4&gt;
&lt;p&gt;Walking is easy. You need regular cloth and shoes. You don’t need trainers or special equipment. You don’t even need a fitness tracker (however it did help &lt;strong&gt;me&lt;/strong&gt; to kick-start this habit) - your regular smartphone probably have a built-in pedometer in it. Just take your smartphone, a pair of headphones and a nice audiobook or podcast - and go for a walk. You can even skip the headphones and the audiobook and just walk - looking around, wandering, taking turns you never would have taken. Just make sure your GPS works, so you won’t get lost in your first explorations of the neighborhood.&lt;/p&gt;
&lt;p&gt;I tend to split my walks to two segments: one in the day and on in the evening. By taking a daily walk you also expose yourself to the sun which is important in order to get vitamin D, and since It’s hard to carve 1.5 hours of free time during a working day, I tend to do my longer walk at the evening, when I’m done with work. It also helps me do a mental shift between work and home. I spend most of my time in front of my computer, and sometimes It’s hard to do a mental shift between work and hobbies since I literally stay at the same place when my work day is over. Some people like to dress up to work even when WFH, and then when they finish work, they switch to their home cloth to signal their brain of a mental shift from work to home mode. I find it too much of a hassle, however taking a walk does the trick for me. If you have a dog, take it with you - it does good to dogs as well.&lt;/p&gt;
&lt;p&gt;I also suggest you start small. Identify how much you are doing right now. It can be 2k or it can be 7k. And then try to increase that number by 1k for each week you were able to do it consistently. If you fail at your next goal, downgrade it back by 1k and do that for another week. For each intermediary goal - you should &lt;strong&gt;not&lt;/strong&gt; feel any muscle pain or extreme struggle to do it. If you feel muscle pain the next day, or you find it hard to reach your goal, keep doing it until you find it easy and only then switch to the next thousand.&lt;/p&gt;
&lt;h4 id=&quot;2-walking-is-better-than-nothing&quot;&gt;2. Walking is better than nothing&lt;/h4&gt;
&lt;p&gt;There is no guideline as to how much time you need to put into active lifestyle. And I’m not a doctor - so consult with one first, but the consensus is that you should do moderate intensity aerobic and anaerobic activities. However, sometimes its hard, some days we feel lazy, and not everyone can workout daily. And walking is better than slumping on the couch.&lt;/p&gt;
&lt;p&gt;Don’t stress with the 10k steps &lt;strong&gt;a day&lt;/strong&gt; goal if you don’t feel like it. I decided for myself that on days when I’m working out - I might not reach 10k steps. In the end my time in the day is limited and if I did 1-hour workout, I might go for a 30-minute walk that day instead of the full 1.5 hours. If I did a 20km bike ride, I might skip the walking for that day. Because the end goal is to stay active and healthy, so I don’t want to stress about it if I’m tired after a workout or had a busy day and was able to dedicate only 1 hour for activity that day. But I do try to stay active &lt;strong&gt;every day&lt;/strong&gt; whether its aerobic or anaerobic activity, or walking 10k steps.&lt;/p&gt;</content:encoded></item><item><title>Vegan-ish March challenge</title><link>https://kudmitry.com/articles/vegan-ish-march-challenge/</link><guid isPermaLink="true">https://kudmitry.com/articles/vegan-ish-march-challenge/</guid><pubDate>Tue, 06 Apr 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Obligatory Disclaimer - I’m not a doctor or dietitian. This post does not substitute consultation with a professional. This post is not a call to action and should be read for entertainment purposes only.&lt;/p&gt;
&lt;p&gt;On March I decided to convert to Veganism (with caveats, more on it later).&lt;/p&gt;
&lt;p&gt;Why you ask? As far as I know, there are two reasons why people convert to veganism.&lt;/p&gt;
&lt;h3 id=&quot;ethical-reason&quot;&gt;Ethical Reason&lt;/h3&gt;
&lt;p&gt;It’s no secret that meat and dairy industries are cruel. Cruel in the way they pollute our atmosphere (&lt;a href=&quot;https://ourworldindata.org/greenhouse-gas-emissions-food&quot;&gt;https://ourworldindata.org/greenhouse-gas-emissions-food&lt;/a&gt;); cruel in the way they use our land (&lt;a href=&quot;https://ourworldindata.org/land-use&quot;&gt;https://ourworldindata.org/land-use&lt;/a&gt;) which leads to forest destruction; and cruel in the way they treat the animals (I’ll save you from sources, but they are easily available online).&lt;/p&gt;
&lt;p&gt;An ethical vegan is a person whose eyes were opened by a documentary they’ve accidentally seen or an article they’ve read - about the cruelty of the meat industry. Armed with the desire to save our planet and stop animal suffering, our individual decides to adopt veganism.&lt;/p&gt;
&lt;p&gt;I, too, care about our planet. I try to sort my garbage and avoid plastic bags. I prefer to take a walk or ride my bicycle instead of driving a car. But I do it as long as it does not harm my comfort too much. If I forgot to bring a reusable bag with me, while going shopping, I’ll take a plastic one from the shop as opposed to fill my pockets with apples.&lt;/p&gt;
&lt;p&gt;I also know that many industries are bad. All the electronics we use require tantalum which is a chemical that &lt;a href=&quot;https://en.wikipedia.org/wiki/Tantalum#Status_as_a_conflict_resource&quot;&gt;still obtained unethically&lt;/a&gt;. Even things we take for granted such as electricity, still comes mainly from the polluting fossil fuels (&lt;a href=&quot;https://ourworldindata.org/fossil-fuels&quot;&gt;https://ourworldindata.org/fossil-fuels&lt;/a&gt;). And yet, I haven’t got rid of my phone and left society to live off grid in a tent in the forest.&lt;/p&gt;
&lt;p&gt;So as long as something is good for me at it wasn’t obtained 100% unethically, like a stolen item, I’d probably ignore the fact that it harms our planet.&lt;/p&gt;
&lt;p&gt;And meat is good for us, right?&lt;/p&gt;
&lt;h3 id=&quot;health-reason&quot;&gt;Health Reason&lt;/h3&gt;
&lt;p&gt;The second reason people become vegans is associated with health. There is this myth that people can not survive without meat. Meat is the source of important nutrients like protein or Vitamin B12. You most likely heard about a friend of a friend who became vegan and now need to take supplements because he can’t source B12 from plants. How a diet with supplements can be healthy?&lt;/p&gt;
&lt;p&gt;Well, the fact is - it can. Protein is largely available in plants (&lt;a href=&quot;https://www.medicalnewstoday.com/articles/321474#15-best-vegan-proteins&quot;&gt;https://www.medicalnewstoday.com/articles/321474#15-best-vegan-proteins&lt;/a&gt;) but B12 indeed can not be obtained from plants. It also can not be obtained from animals, at least not directly.&lt;/p&gt;
&lt;p&gt;Vitamin B12 is produced by bacteria that lives in our soil and water (&lt;a href=&quot;https://en.wikipedia.org/wiki/Vitamin_B12#Sources&quot;&gt;https://en.wikipedia.org/wiki/Vitamin_B12#Sources&lt;/a&gt;) and since we wash our veggies and filter our water - we kill these bacteria (along with a lot of other harmful bacteria). But how animals get it then? In two ways - they either live a happy and free life and eat unwashed grass with dirt and drink unfiltered water which contains these bacteria; or they get supplements. The same supplements that vegans get.&lt;/p&gt;
&lt;p&gt;The meat industry has a very good marketing campaign to convince us that meat is a necessity. At least that’s what I though, before I saw a random documentary about plant based diet.&lt;/p&gt;
&lt;p&gt;And among all the things I dislike, I very much dislike when I’m being lied to. And I also want to be a super human. So armed with knowledge, determination and excitement - I’ve started my vegan Month.&lt;/p&gt;
&lt;h2 id=&quot;how&quot;&gt;How?&lt;/h2&gt;
&lt;p&gt;As with my &lt;a class=&quot;internal new&quot; href=&quot;/articles/no-shopping-february/&quot;&gt;February challenge&lt;/a&gt; I needed to set rules. And based on my rules, I’ve come to realize that I’m not 100% pure vegan, hence the challenge was called “Vegan-&lt;strong&gt;ish&lt;/strong&gt; March”. Here are my rules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No meat products [*obviously, duh..]*&lt;/li&gt;
&lt;li&gt;Up to 1 time fish per week during social events [&lt;em&gt;I don’t remember why I decided that fish is fine, but spoiler alert - I did not eat any. I don’t like fish in general&lt;/em&gt;]&lt;/li&gt;
&lt;li&gt;Ghee butter is fine in moderation [&lt;em&gt;My wife started to use Ghee while cooking, so not to complicate things in the kitchen, I included this rule&lt;/em&gt;]&lt;/li&gt;
&lt;li&gt;No eggs&lt;/li&gt;
&lt;li&gt;No dairy products&lt;/li&gt;
&lt;li&gt;First two weeks up to one pizza with no meat [&lt;em&gt;I wanted to make the transition easier and not be an asshole during social events when everyone wants pizza, and I’m like “I’m vegan”&lt;/em&gt;]&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before we proceed to the results, lets talk about my relationship with food.&lt;/p&gt;
&lt;p&gt;Food for me was a source of energy. It was something I had to do in order to live a good life. I usually ate meat at least once a day and had this idea in my head that I &lt;strong&gt;need&lt;/strong&gt; meat on daily basis in order to stay healthy. I focused on chicken, and ate red meat only at restaurants where they could prepare a good steak or an amazing hamburger. I dislike fish, so I rarely ate it. Dairy products in general make my stomach upset, so I’ve limited their usage. Eggs for me were nice for quick breakfasts.&lt;/p&gt;
&lt;p&gt;It is recommended to switch to vegetarian diet first, and not jump towards veganism right away. But since I’m not your typical meat eater who eat 5 portions of meat a day, and I mostly live by the rule of “all or nothing”, I’ve decided to jump straight into veganism.&lt;/p&gt;
&lt;h2 id=&quot;the-results&quot;&gt;The Results&lt;/h2&gt;
&lt;h3 id=&quot;timeline&quot;&gt;Timeline&lt;/h3&gt;
&lt;p&gt;The first few days were amazing. I was energized, excited and ready to conquer my challenge on the way to become a better human being. I was armed with knowledge and science and instead of simply eliminating meat for my meals, I’ve looked for replacements in forms of beans, grains, legumes and nuts.&lt;/p&gt;
&lt;p&gt;But then, by the end of first week, I’ve started to feel cloudy and tired. I’ve started to suspect that maybe I don’t eat enough and have nutrient deficiency, so at day 8, I’ve started to count calories.&lt;/p&gt;
&lt;p&gt;Counting calories helped, I did realize that 100g of bell pepper is not the same as 100g of chicken breast and that I do have nutrient deficiency. Armed with this new knowledge, I’ve educated my self even more and started to plan my meals based on the nutrients I need.&lt;/p&gt;
&lt;p&gt;And things started to become better. I was more energized, felt great, no more cloudy. Until day 12, when life happened. As with my February challenge, I’ve broken my diet on day 12.&lt;/p&gt;
&lt;p&gt;I’ve met with a friend, and we went to a bar for a couple of cocktails and food. And the only food they had - was pizza. We were hungry, my friend wanted to share the pizza, and I was weak. Technically, pizza falls under rule (6) but at that point I was doing great with my diet and decided mentally that I was not going to break it with pizza - but I did.&lt;/p&gt;
&lt;p&gt;Other than this, no major accidents. I feel better, I’m more energized (according to my wife) and I started to like food and eating.&lt;/p&gt;
&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h3&gt;
&lt;h4 id=&quot;bowl-movement&quot;&gt;Bowl Movement&lt;/h4&gt;
&lt;p&gt;The messy part. Skip this if you don’t want to discuss poop.&lt;/p&gt;
&lt;p&gt;My bowl movement before vegan diet - was regular, but it wasn’t.. perfect. I though maybe it’s related to meat and was hoping that plant based diet will help with that - but I was disappointed. It didn’t make things worse, but it didn’t improve either.&lt;/p&gt;
&lt;p&gt;However, one positive effect that I did notice, is that I now rarely have abdominal distension. I used to have it pretty often when I was eating meat, and it did bother me. Not anymore.&lt;/p&gt;
&lt;h4 id=&quot;energy-levels&quot;&gt;Energy Levels&lt;/h4&gt;
&lt;p&gt;One more benefit of plant based diet I’ve read about is that you have more energy. Can do more things, is less tired and can lift more if you are working out.&lt;/p&gt;
&lt;p&gt;And while I’m unable to judge my levels of energy - I’m still functioning normally, do my job, work out and relax; my wife claims I’m way more energized. I was hoping to have more energy for when I work out, but again, I’m unable to judge objectively. I do feel stronger, but it can be also because I train regularly.&lt;/p&gt;
&lt;p&gt;However, one big benefit that I do notice from plan based diet - is that I’m no longer tired after eating. Do you know this feeling, after when you eat a meal with meat, you suddenly feel &lt;em&gt;full&lt;/em&gt;? You feel sleepy and tired ready for siesta even if its 7PM? Well, I used to have that. But after I’ve switched to plant based diet - I’m no longer having this feeling. I can eat at 4PM, I cat eat at 7PM and I won’t feel tired, sleepy or full.&lt;/p&gt;
&lt;h4 id=&quot;no-pain&quot;&gt;No pain&lt;/h4&gt;
&lt;p&gt;Yet another benefit of plan based diet is that people who have joint pain, experienced less pain or even no pain after switching to plant based diet. I’m not sure about the science behind it, but nevertheless I have chronic shoulder pain that no doctor was able to fix or even diagnose, so I’ve learned to live with it. Plant based diet did not help to relief this pain.&lt;/p&gt;
&lt;h4 id=&quot;the-love-for-food&quot;&gt;The love for food&lt;/h4&gt;
&lt;p&gt;After switching to plan based diet, I fell it love with food and cooking. Dinners suddenly became colorful with a bunch of vegetables. I’ve educated myself about sources of iron, protein, calcium in plants. I even counted calories for some time (I’ve quit it about 4–5 days before the end of the months, as it’s too time-consuming for me).&lt;/p&gt;
&lt;p&gt;I’ve started to enjoy chopping and cooking; started to love the taste of food. I spend way more time in the kitchen and I did order way less take away food during this month.&lt;/p&gt;
&lt;p&gt;Now you can say “&lt;em&gt;Jeez Dmitry, you could have developed the same love for food and cooking by eating meat as well, should you cared enough&lt;/em&gt;” and I’d agree with you, I could. But for me, there is a mental association between vegetables and healthy food. And eating healthier food make me want to prepare this food as well. Sometimes it takes a change to &lt;a class=&quot;internal new&quot; href=&quot;/articles/compound-effect-and-habits/&quot;&gt;kick-start a new hobby or habit&lt;/a&gt;, and plant based diet did that for me. So +1 for plant based diet.&lt;/p&gt;
&lt;h4 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h4&gt;
&lt;p&gt;The best indicator of health that we can get relatively easily - is blood test. I have blood test I’ve done around end of January, beginning of February. But I haven’t yet done one after being vegan. I don’t think one month is enough to have a noticeable effect, so I’ll do a test in few months.&lt;/p&gt;
&lt;p&gt;Another interesting fact is that I’ve lost around 2.5kg (~5.5 pounds for my non-metric readers). I suspect I was not eating enough the first days and only when I started to count calories, I’ve realized that I need to eat more. It’s hard for me to gain weight and I have a theory about it: it takes the body some time to adjust to new diet. It’s like a shock since my body was used to meat and suddenly it’s not getting it anymore. So it started to burn it reserves. Not sure if it makes sense, but I’ll keep monitoring my weight, and will contact my doctor if I won’t see any improvements.&lt;/p&gt;
&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;
&lt;p&gt;The month is over, so as my diet. I proved to myself that food can be tasty, diverse and healthy.&lt;/p&gt;
&lt;p&gt;But I also realized that plant based diet is not easy. Because I don’t eat fish, I’m taking Omega supplements. But only after few days of my challenge, I’ve realized that Omega supplements are not vegan since they come from fish oil.&lt;/p&gt;
&lt;p&gt;Somewhere around the beginning of March, I went to a local cafe with my wife, and we ordered a couple of sandwiches but asked if they can be made without mayonnaise since we are vegans. The waitress said that it is possible. After we sat down at the table, the waitress approached us and asked “is honey fine, or to take it out as well?”. I was baffled. What’s the problem with honey? Only later I’ve realized that honey is technically non-vegan because it involves bee exploitation.&lt;/p&gt;
&lt;p&gt;Eating proper vegan is hard. There are a lot of great vegan supplements for cheeses, burgers, sausages and even mayonnaise; but the idea of my challenge was not to replace processed meat with processed vegan food. While I do believe that a vegan burger is healthier than the average meat burger, they are both processed foods.&lt;/p&gt;
&lt;p&gt;It also requires you to plan what you eat. Beans for example needs to be soaked for at least 12 hours before they are being cooked for another 60–90 minutes.&lt;/p&gt;
&lt;p&gt;Am I converted? I’m not sure. I find a lot of benefits in plant based diet ranging from the taste, flavor and the process of preparing food; all the way to health benefits like not feeling tired or full after a meal. But food is a lot more than energy. Food is culture; food is experience. I’m yet to find a vegan restaurant that will give me the same experience as a restaurant that can serve me a great steak with melting butter and rosemary together with a glass of red wine.&lt;/p&gt;
&lt;p&gt;Food is embedded in our culture and while I don’t crave steaks, I’m not ready to give up the experience, the same way I enjoy a good beer in a bar, knowing that alcohol is bad for me. So for now, I’m going to stick to my plant based diet while eating at home or even ordering take away, but I’ll reserve a special place for meat when visiting a great restaurant that can serve a tasty steak or an amazing burger.&lt;/p&gt;</content:encoded></item><item><title>Compound effect and habits</title><link>https://kudmitry.com/articles/compound-effect-and-habits/</link><guid isPermaLink="true">https://kudmitry.com/articles/compound-effect-and-habits/</guid><pubDate>Wed, 10 Mar 2021 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the stock market, there is an effect known as compound interest. In a nutshell it works like this: when you invest $100 with a an yearly return of 10%, after the first year you will have $110 . However after the second year you will have 121$ (so a growth of $11 compared to 10$ from the first year). After third year you will have 133.1$, 146.41$ after fourth and eventually, after 10 years you will have $259.37 an additional $159.37.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;So the interest rate of 10% yearly applies also to the earnings you’ve made this year. Therefor its called “compound interest” as opposed to a 10% interest that applies only to the initial investment. And if you look at a longer period, you will notice that the growth is exponential.&lt;/p&gt;
&lt;h3 id=&quot;stocks-what&quot;&gt;Stocks? What?!&lt;/h3&gt;
&lt;p&gt;Yeah, right. Back to habits. See, habits work the same way. When you break some habit, or fall into a bad habit, it creates the same compound effect. At some point you wake up and ask yourself “How did I get there?”. But what happened in retrospect is that one day you’ve missed the gym, and since you’ve missed the gym you decided to allow yourself to eat a burger instead of a salad, with beer instead of water, in front of a TV instead of working on your hobby. And the next time you need to go to the gym, you are like “But I’ve already missed one session, so what’s the harm in missing this one as well”. And then you wake up and ask yourself “How did I get there?”.&lt;/p&gt;
&lt;p&gt;Of course, it’s not that fatal. Missing one gym session won’t throw you to the bottom of life, and it depends on your self motivation and self-control. I, for example, struggle to start the work week after the weekend. Even if just before weekend I’ve been super engaged with work, weekends throws me off my track and starting the week is hard for me.&lt;/p&gt;
&lt;p&gt;There is an unwritten rule in the habit making world that says “Never miss a habit &lt;strong&gt;two&lt;/strong&gt; times in a row”. So if you’ve missed the gym, you &lt;strong&gt;must&lt;/strong&gt; go the next time. Compound effect takes time to accumulate. One missed habit will shake you a bit, but usually, if you have strong self-control, it should not put you off track. A habit missed twice - is harder to recover from. And the scary thing is that once you break one habit - others start to break as well. Compound effect.&lt;/p&gt;
&lt;h3 id=&quot;the-good-part&quot;&gt;The good part&lt;/h3&gt;
&lt;p&gt;But there is a good part as well. Compound effect works the same way with good habits. You start to build the habit of working out. At some point you realize that your diet matters so you start to eat healthier. By eating healthier you cook more at home hence saving money on take away as well as investing in a clean kitchen (nobody wants to cook in a disgusting kitchen). By setting the habit of cleaning your kitchen, you expand it to keep your entire house tidy, which leads to getting rid of stuff you don’t need hence being intentional with what you buy (which again, saves money). And it one year you will look back and realize that you’ve changed as a person. Compound effect.&lt;/p&gt;
&lt;h3 id=&quot;start-small&quot;&gt;Start Small&lt;/h3&gt;
&lt;p&gt;The compound effect is very strong. But like anything in life - abuse it, and it will work against you. By tackling many habits at once, you risk the chance to developing none at all, or worse, introducing bad ones. Here are some tips on how you can create new habits and enjoy the compound effect.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start small - focus on one habit. Remember - habit is not something that you do 3 times in a row. Forming a habit takes around &lt;a href=&quot;https://jamesclear.com/new-habit&quot;&gt;66 days&lt;/a&gt;. But it doesn’t mean you should focus only on one habit every 66 days. Get the ball rolling. Once you are comfortable with the habit, introduce new one. For me, it takes around 1 to 2 weeks of daily habits to start sticking. But It’s personal, so see what works for you.&lt;/li&gt;
&lt;li&gt;Know your self. By knowing your self, you know how much self-control you have. By knowing how much self-control you have, you know how to tackle a broken habit. I personally know that it’s way harder for me to come back to a habit if I missed it 2 times in a row. For some of you it could be 4 times, for others 1. There is no magic number, it entirely depends on your self-control. Experiment and see what works for you.&lt;/li&gt;
&lt;li&gt;Don’t be hard on yourself. When you miss a habit, don’t beat yourself. You are &lt;strong&gt;not&lt;/strong&gt; a failure for missing a habit. Life happens so your Monday gym session might fall on a period when you are sick, overwhelmed with work, on vacation or just lazy. It’s not the end of the world. Make adjustments and reschedule. Remember - It’s ok to miss a habit, it’s not ok to break it. So reschedule according to your magic number from point 2 above.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remember, habits are very individual. Individual in the way &lt;strong&gt;how&lt;/strong&gt; you build them and individual in the way &lt;strong&gt;why&lt;/strong&gt; you build them.&lt;/p&gt;
&lt;p&gt;Don’t create habits because someone told you it changed their life. Remember, it changed &lt;strong&gt;their&lt;/strong&gt; life. It might not change yours. Always start with self discovery. Realize &lt;strong&gt;why&lt;/strong&gt; you want to build a habit. You want to be active and live a healthy life? So find a physical habit. Don’t run to the gym because someone told you that you must do that. Not everybody like the gym. There are plenty of other physical activities like cycling, rowing, running, yoga, boxing, HIIT and etc. Find what works for &lt;strong&gt;you&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once you found it, experiment on &lt;strong&gt;how&lt;/strong&gt; to implement it. You have 1 free evening? So go to the gym once a week (or do any other physical activity that works for you). Sure somebody will tell you that once a week is not enough because they go at least 5 times a week. But again, it’s their life. They might have 5 evening available because they work on a different job than you or have a different lifestyle than you. Again it’s &lt;strong&gt;your&lt;/strong&gt; life. Once a week is always better than none. You can’t enjoy the compound effect on zero - because any % from zero is still zero. Start the ball rolling and you will adapt your life. Sure it will take you more time to reach the same results as the 5 times a week guy/gal, but you are not your single habit - you are the sum of everything you do and don’t do in life, and no two lives are similar. Always remember this.&lt;/p&gt;</content:encoded></item><item><title>No shopping February</title><link>https://kudmitry.com/articles/no-shopping-february/</link><guid isPermaLink="true">https://kudmitry.com/articles/no-shopping-february/</guid><pubDate>Thu, 04 Mar 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Back in January I caught myself scrolling and favorite-ing a lot of products on Amazon. And I thought to myself - what if I decide to do a month without shopping?&lt;/p&gt;
&lt;h2 id=&quot;rules&quot;&gt;Rules&lt;/h2&gt;
&lt;p&gt;But first, some rules. It’s easy to say “I’ll just do a no shopping February”, but you can’t simply commit to a challenge without setting some rules. Are you going to buy groceries? Gifts? Medicine? What about self treatment procedures like gym visit?&lt;/p&gt;
&lt;p&gt;You need to have rules. Rules makes your challenge way easier to follow. For me, February is a special month because it has 3 important occasions: My Birthday, St. Valentines Day and Dating anniversary. So I defined the following rules: I’ll avoid any shopping except the following -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Birthday present for my self&lt;/li&gt;
&lt;li&gt;St. Valentine’s Day present for my wife&lt;/li&gt;
&lt;li&gt;Dating anniversary present for my wife (should we decide to give gifts this year)&lt;/li&gt;
&lt;li&gt;Replacement of a broken / damaged item that was previously in use at least once during 2021&lt;/li&gt;
&lt;li&gt;Groceries and Food take away&lt;/li&gt;
&lt;li&gt;Up to 1 book&lt;/li&gt;
&lt;li&gt;Anything for self-care (so medical treatments, haircut, gym etc)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-result&quot;&gt;The result&lt;/h2&gt;
&lt;p&gt;I’m glad to report that I was able to follow the challenge through entire 28 days of February but with one exception. I’ve broken the rule once.&lt;/p&gt;
&lt;p&gt;I use comfy slippers to wander around the house. However, since they are made of synthetic, medium grade quality, materials - they became stinky. So stinky, that by the middle of the day my socks were stinky as well. So I decided to replace them. Looking back at my rules, they fall under rule (3) - I did use them daily, and the fact they started to stink can be categorized as “damage”. I went to an online shopping store, found the slippers I want and was ready to order them. Then I realized that the shipping price was almost half the price of the slippers and more over, if I’d add another item to reach a certain sum - I’d get free shipping. Damn you marketing!&lt;/p&gt;
&lt;p&gt;I did cost benefit analysis and came to a conclusion that I’d rather pay for another item I need than for shipping - so I’ve decided to order pants, but I’ve told to myself that they will replace existing pants I already have (a rule I try to follow to minimize clutter introduction in my life - for every clothing item I buy, one should go away).&lt;/p&gt;
&lt;p&gt;So yes, I’ve broken the challenge. But I’ve realized something.&lt;/p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;I’m not addicted to shopping. I don’t spend countless amount of money to hoard items. I could improve my impulsive buying habits, but I was not doing this challenge to cure some addiction or cut on expenses. I did it purely for educational and “scientific” knowledge. And I’ve learned two things.&lt;/p&gt;
&lt;h4 id=&quot;control-your-impulses&quot;&gt;Control your impulses&lt;/h4&gt;
&lt;p&gt;I did have impulsive buying habit. When I’ve decided that I want or need something, I usually went to buy it. Which then lead to buyer remorse since by the time this item arrived (I usually buy online) I already was past the initial dopamine rush from the purchase, and I was wondering “why the hell did I bought this”. Doing this challenge, I’ve realized that nothing is really that urgent. I was aware of instant gratification, delaying your purchases and etc, but somewhere during this pandemic - I’ve lost it. This challenge served as a restart for me. A restart to remind me that I have everything I need and nothing is urgent to buy.&lt;/p&gt;
&lt;h4 id=&quot;life-happens&quot;&gt;Life happens&lt;/h4&gt;
&lt;p&gt;I’ve stated in the beginning of this post how important it is to set up rules. Rules define the boundaries in which you can operate. They define your actions and prevent you from having the feeling of being a failure because there is a difference between “No shopping” and “No shopping except for…”. I don’t believe it’s correct to change your rules every time you encounter a road block, but it’s also not healthy to never change your rules. Life is dynamic, and adapting to it is one of the best traits you can develop in yourself. Adaptability is what helps us survive. And this challenge reminded me of this important lesson.&lt;/p&gt;
&lt;p&gt;By settings strict rules and promising to never adapt them - you are doomed to failure because at some point, life will kick you to the floor, and you will need to adapt. On the other hand, by constantly changing your rules every time you face a difficult situation, you will end up as a weak person who has no moral stance, no rules, to beliefs to live by.&lt;/p&gt;
&lt;h2 id=&quot;closing-words&quot;&gt;Closing Words&lt;/h2&gt;
&lt;p&gt;Doing this challenge reminded me of two important lessons: Delayed gratification and Adaptability in life.&lt;/p&gt;
&lt;p&gt;It’s already March, and I’m 4 days to my new challenge. Stay tuned for March end to find out about it :)&lt;/p&gt;</content:encoded></item><item><title>I did 33 days of &quot;social media on my phone detox&quot; challenge</title><link>https://kudmitry.com/articles/i-did-33-days-of-social-media-on-my-phone-detox-challenge/</link><guid isPermaLink="true">https://kudmitry.com/articles/i-did-33-days-of-social-media-on-my-phone-detox-challenge/</guid><pubDate>Fri, 26 Feb 2021 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On 19th of January I’ve had enough. I was stressed. Stressed mainly because of overusing my phone for both social media (Reddit and YouTube in my case) and for news checking. So I decided to do what everyone else would recommend you to do once you reach this point - delete social media from your phone.&lt;/p&gt;
&lt;p&gt;So I’ve deleted Reddit, disabled YouTube (thank you big phone manufacturer that preinstall certain apps on their phones without the ability to uninstall them, only disable) and blocked the only news website I was using (this block included block on PC as well). And I’ve created a fancy tracking calendar, hang in on my cork board and every day crossed out the previous day, in case I’ve managed to stay away from social media on my phone, which I’m happy to report, I did for 33 days in a row.&lt;/p&gt;
&lt;p&gt;In order for it to make sense, I first need to tell you about my relationship with my phone and social media (from now on, when I say social media I mean Reddit and YouTube since I use Facebook mainly for marketplace abilities and mainly on PC, and I don’t use Instagram or any other social platform).&lt;/p&gt;
&lt;p&gt;You see, Reddit for me was a feed of “inspirational” pictures. I like Living spaces, I like mechanical keyboard and watches, I like computers and networking gear. So I’m subscribed to &lt;a href=&quot;http://reddit.com/r/battlestations&quot;&gt;/r/battlestations&lt;/a&gt;, &lt;a href=&quot;http://reddit.com/r/EDC&quot;&gt;/r/EDC&lt;/a&gt;, &lt;a href=&quot;http://reddit.com/r/malelivingspace&quot;&gt;/r/malelivingspace&lt;/a&gt; and similar - which all are picture based. So when you open the Reddit App you have a nice picture feed of EDC items, battlestations and etc. A great time filler for when you are bored. But then little by little my habits started to slip. I’d finish scrolling my feed in few minutes but was still bored, so I started to glance at &lt;a href=&quot;http://reddit.com/r/all&quot;&gt;/r/all&lt;/a&gt; or &lt;a href=&quot;http://reddit.com/r/popular&quot;&gt;/r/popular&lt;/a&gt; which now made me an expert in US politics (and I don’t even live there!) which obviously contributed to more stress (as politics are never rainbow colors). Add to that the fact that my lunch or dinner would be accompanied by the one and only - YouTube. I felt that I was either working or doing something else that involved Reddit / YouTube / New reading.&lt;/p&gt;
&lt;p&gt;A word on PC usage. When I’m on my PC, I’m doing one of 2 things - I either do work for my day-to-day job or work on my hobbies. So I was more mindful about the way I use Reddit or YouTube there. Add since Reddit on PC is not that useful as picture feed, I rarely visited it from my PC.&lt;/p&gt;
&lt;h3 id=&quot;the-a-ha-moment&quot;&gt;The “A-Ha!” moment&lt;/h3&gt;
&lt;p&gt;On a regular evening, I was driving my wife to therapeutic massage, after which she is usually very relaxed and advised not to drive. While she was doing the therapy, I was walking (a habit I’m trying to build) and listening to an audiobook. Closer to the end of the 1-hour mark, I went back to the car to make sure that my wife won’t have to wait for me and by that time I’ve finished listening to one chapter of the book and was not interested in starting a new one. I entered the car and started to wait. And at that time, the “A-Ha!” moment, also known as Eureka, came to me.&lt;/p&gt;
&lt;p&gt;When people ask for suggestions about phone addiction, and mainly constant Instagram or Facebook scrolling - they are being told to “Just delete these apps. You won’t miss anything”. And it’s true, I didn’t miss. All the important news I’ve “missed” - were filled to me by my friends; the YouTube videos were still waiting for me when I used YouTube on my PC; and the Reddit posts - well.. They are just nice time fillers. And while I agree that being exposed to nice houses or beautiful wardrobes, can shape the way you decorate your house or dress (you can read more about my “dressing up” experiences in this blog post I’ve published - &lt;a class=&quot;internal new&quot; href=&quot;/articles/you-are-not-your-news-feed-or-maybe-you-are/&quot;&gt;You are not your newsfeed! Or maybe you are?&lt;/a&gt;) I believe the good ol’ saying of “Everything is good in moderation” applies here very well. The benefits of nice houses or computers, were overtaken by the amounts of stress I was feeling, and I prefer to be stress-free rather than having a nice “battlestation”.&lt;/p&gt;
&lt;p&gt;So here I was, sitting my car, &lt;strong&gt;bored&lt;/strong&gt;, checking WhatsApp every 20 seconds, checking my calendar every 30 seconds (and no, don’t get me wrong, I’m not super busy CEO whose schedule changes every 10 minutes) - while waiting for my wife to arrive so we could drive back home.&lt;/p&gt;
&lt;p&gt;And at this moment I’ve realized - &lt;em&gt;We have it backwards&lt;/em&gt;! Deleting social media apps from our phones is like taking painkillers every day because you have a headache - sure the &lt;strong&gt;symptom&lt;/strong&gt; goes away, you are no longer addicted to social media (or have headaches) but the problem stays there - something is wrong with your health (otherwise you wouldn’t have constant headaches) and you &lt;strong&gt;still don’t know how to be bored&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;See, the reason why I’ve used my phone while eating or when waiting for my wife in the car is because I didn’t know how to enjoy food or be bored. Yes, I’ve deleted Reddit and YouTube and I didn’t use those anymore, but I was still reaching to my phone during those 33 days just to check WhatsApp, look at package tracking or stock prices, a set of absolutely random and useless actions that are the by product of not knowing how to live in the moment, enjoy simple things like a meal or enjoying being bored.&lt;/p&gt;
&lt;p&gt;So whats next? I’m done with the challenge. I’ve enabled YouTube once again because I was sick and in bed, couldn’t sleep, so decided to watch some YouTube videos. I occasionally check the news (every block can be bypassed). I still didn’t install Reddit, but I guess it will happen sooner or later. I think this challenge was worth the conclusion I’ve came too. The message of deleting social media for “addicted” people is so strong that it perceived as the ultimate solution for addiction while my experience tells the opposite - its just eliminating the symptoms, but the problem stays. I don’t reach to my phone when I’m truly living in the moment (being in the Flow, having a deep conversation, watching a movie or etc) so the problem is not Reddit or YouTube on my phone - the problem is my relationship with boredom. I guess I’ll have to work on that.&lt;/p&gt;
&lt;p&gt;Disclaimer: Obviously I’m not an expert on addictions. My experience might not apply to you and by eliminating social media from your life, you might “heal” your addiction. It all depends on the answer to the question “Why do you use it in the first place?”. I’m not having anxiety because my social feed is filled with younger, more successful people than me, nor I suffer from &lt;a href=&quot;https://en.wikipedia.org/wiki/Fear_of_missing_out&quot;&gt;FOMO&lt;/a&gt; as my social apps are not associated with family or friends (like Instagram or Facebook). But if your main issues are stress or anxiety from FOMO or constant comparison to younger (or same aged) people, who “look” more successful than you - then yes, deleting social media might do wonders to you. So as always, your mileage may vary.&lt;/p&gt;</content:encoded></item><item><title>Wrap your gifts not your dependencies</title><link>https://kudmitry.com/articles/wrap-your-gifts-not-your-dependencies/</link><guid isPermaLink="true">https://kudmitry.com/articles/wrap-your-gifts-not-your-dependencies/</guid><pubDate>Mon, 08 Feb 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We’ve all been there. Its time to introduce a new package / dependency to our code base, be it a HTTP request library, a logger or something else, and the question we ask ourselves “&lt;em&gt;Should I wrap it?&lt;/em&gt;”&lt;/p&gt;
&lt;p&gt;The main reason we ask this question is a hidden fear that maybe in the future we would like to change the underlying implementation to a different package. We, humans, like to keep our options open.&lt;/p&gt;
&lt;p&gt;But in reality, by creating wrappers around your dependencies, you are actually narrowing down your options and limiting your self.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Lets say you use &lt;a href=&quot;https://www.npmjs.com/package/axios&quot;&gt;axios&lt;/a&gt; package and instead of using it directly, you decide to wrap it. Your wrapper will depend on axios and will expose some generic API that uses the internal functionality of axios.&lt;/p&gt;
&lt;p&gt;You need to find the exact balance between what you want to expose and what you want to abstract. You don’t want to create a dumb proxy in front of axios because you want to keep your options open in case you decide to switch underlying functionality, so you try to create something abstract. And by doing so you encounter the first problem of wrappers:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wrappers take a feature rich library and dumb it down a set of simple functions&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You don’t have time to create a proper wrapper. You don’t know what functionality you might need from it, so you focus on wrapping what you need &lt;strong&gt;now&lt;/strong&gt;. In the end, you are making a product for your clients, not developing a library. You don’t have time to correctly wrap the underlying dependency. So you expose a few methods, those you need right now, in some abstract way, type &lt;em&gt;git commit&lt;/em&gt; and go to drink your coffee with a smile on your face.&lt;/p&gt;
&lt;p&gt;Later on, when one of your team mates needs to develop a new feature, he will happily look at your axios wrapper, expecting it to contain a method he needs and will be disappointed to find out that this method is not there. Now instead of focusing on delivering value to your customers, your team mate will have to focus on creating a useless abstraction for a greater cause of “&lt;em&gt;we might replace it in the future&lt;/em&gt;”, and this brings me to the second and biggest problem of wrappers:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wrappers create the illusion of control. The main argument for creating a wrapper is that “we might change to a different underlying dependency in the future”, but guess what? No body, ever, changed from one logger to another; changed from postgres to mysql. And if they did, their wrapper probably caused them more troubles than help.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In programming there is a nice principle called YAGNI — You Aren’t Gonna Need It. YAGNI suggest that instead of implementing a bunch of abstractions with the hope that &lt;em&gt;“one day I might change the underlying implementation”&lt;/em&gt; you actually focus on implementing what you need now. Don’t be afraid of refactoring — its a healthy process in software development. Focus on implementing &lt;strong&gt;real&lt;/strong&gt; safety nets such as continuous automated unit and integration tests and look at type checking, either via statically typed languages or by using tools like TypeScript to help you with refactoring. Remember — wrappers create the &lt;strong&gt;illusion&lt;/strong&gt; of safety, as they rarely help you with transition from one underlying library to another.&lt;/p&gt;
&lt;p&gt;Donald Knuth once said&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; &lt;strong&gt;premature optimization is the root of all evil&lt;/strong&gt; (or at least most of it) in programming.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Wrapping your dependencies is a premature optimization for a dooms day when you decide to switch to a different logger. This day will either never come or if it does, your wrapper will not save you from tons of refactoring and fixing broken tests anyway. And in the mean time, you’ve wasted time on creating your useless wrapper but instead you’ve needed to focus on proper refactoring and tools to help you with them (such as type checking), proper automated unit and integration testing while in same type enjoying the full potential of a library that’s been co-created by 260 different contributors (&lt;a href=&quot;https://github.com/axios/axios/graphs/contributors&quot;&gt;https://github.com/axios/axios/graphs/contributors&lt;/a&gt;), but hey! It is sure better to roll your own, dull wrapper, ain’t it?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So stop creating wrappers. Instead focus on refactoring (by prioritizing it as tech debt in your development life cycle), focus on tests. Focus on creating middlewares to common functionality that can be quickly plugged to existing libraries, instead of wrapping those libraries. Focus on bringing value to your customers instead of reinventing the wheel over and over again.&lt;/p&gt;</content:encoded></item><item><title>The most important personal quality - Integrity</title><link>https://kudmitry.com/articles/the-most-important-personal-quality-integrity/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-most-important-personal-quality-integrity/</guid><pubDate>Sun, 04 Aug 2019 09:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;A taxi driver went out of his car, stood near the door, opened his pants zipper and started to pee. A municipality worker that happened to be there at the same time, noticed the situation and issued a fine for the taxi driver. As soon as the driver noticed the municipality worker, he took a bottle that was nearby and started to pour water from it over his hands. The taxi driver refused to pay and went to trial. In front of the judge, he told the following story: The wheel by the drivers’ door broke, so he stopped to replace it and took instruments from the trunk. Before leaving the place, he took a bottle of water from the trunk to wash his hands. The judge did not believe him. He said, “if you wanted to wash your hands, why would you take the bottle out of the trunk, move to the drivers’ door and wash it there and not by the trunk?” The driver paid the fine. - Local newspaper&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is a real story from one of the local city newspaper I’ve read some time ago. And I ask my self. The guy is an adult. I guess somewhere around 30–60 years old. He couldn’t hold his pee. So he peed on the street, which is punished by a fine. What would make him lie like a 3-year-old?&lt;/p&gt;
&lt;p&gt;Well, I know what. He wanted to pee. There was no toilet nearby, so he peed on the street. This is punishable by a fine. But who wants to pay a fine? So he lied, with a hope to get by and not pay the fine.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The quality of being honest and having strong moral principles; moral uprightness. - Definition of integrity (noun)&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&quot;integrity-equals-responsibility&quot;&gt;Integrity equals responsibility.&lt;/h3&gt;
&lt;p&gt;Doing something illegal and then lying with a hope to get by with it is called immature. Immature people can not take responsibility. And failing to take responsibility means you are unable to succeed in life because failure to take responsibility – means everything is someone else’s problem. And when everything is someone else’s problem you have no control over the life you are living.&lt;/p&gt;
&lt;p&gt;Children until a certain age are immature. That’s the reason they lie. “&lt;em&gt;But mommy, I didn’t touch it, it fell by itself!&lt;/em&gt;”. Yes, you did. And you are responsible for breaking it. But children don’t get it. They are not evolved enough, yet. They are afraid of telling the truth because they know that they did something wrong, and they might be punished. Nobody want’s to be punished. But parents are not stupid, they know the real story. Vases don’t fall by themselves.&lt;/p&gt;
&lt;p&gt;Having responsibility for your life makes it easier.&lt;/p&gt;
&lt;h3 id=&quot;integrity-equals-peace-of-mind&quot;&gt;Integrity equals peace of mind.&lt;/h3&gt;
&lt;p&gt;The easiest story to tell – is the truth. When you lie, you get caught up in small details that will eventually render your lie to be logically flawed. The taxi driver got caught up because his words made no sense.&lt;/p&gt;
&lt;p&gt;Imagine a life in which you need to remember every small part you’ve ever said, and you also need to think about what you are going to say next so that it will be logical and wouldn’t sound like a lie. And we are speaking about a generation that relies on to-do lists, reminders, and secretaries to remember trivial things like “&lt;em&gt;buy bread on the way home&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;Lying is exhausting. And the more people know or involved in your lies, the more exhausting it is. You suddenly can’t be your self. You can’t share something that happened to you, without thinking about consequences. “&lt;em&gt;Dude, I’ve met yesterday the most amazing girl ever!&lt;/em&gt;”. “&lt;em&gt;Yesterday? But yesterday we played poker, and you said you won’t come because you are sick!&lt;/em&gt;”. “&lt;em&gt;Um… Ah.. mmm..&lt;/em&gt;”. Don’t be that guy. Have integrity.&lt;/p&gt;
&lt;h3 id=&quot;integrity-guides-you-towards-what-is-right-for-you&quot;&gt;Integrity guides you towards what is right for you.&lt;/h3&gt;
&lt;p&gt;When you don’t have integrity, you don’t have any moral principles. Going to hookers? Sure. Stealing candy? Why the hell not!&lt;/p&gt;
&lt;p&gt;Have you ever been presented with two appealing choices and didn’t know what to choose? Well if you have integrity, you’d know what’s important for you. If one of your principles in life is to not drink, then Friday evening in a bar with friends would not be an option for you.&lt;/p&gt;
&lt;p&gt;An abundance of choice is bad. Read &lt;a href=&quot;https://www.amazon.com/Paradox-Choice-More-Less-Revised-ebook/dp/B000TDGGVU/ref=sr_1_1&quot;&gt;The Paradox of Choice – Barry Schwartz&lt;/a&gt; to understand why. Minimizing the number of choices we make in life – makes life easier. And if you don’t have moral principles or integrity, you’d have to make a lot of choices daily because you don’t really have an opinion or preference.&lt;/p&gt;
&lt;p&gt;And no, don’t get me wrong. Sometimes you are presented with two really appealing choices, and they have nothing to do with your integrity or moral principles. Life’s hard after all, there is no magic potion titled “Integrity” that will remove all obstacles. But it will make your life easier.&lt;/p&gt;
&lt;p&gt;Evolution gave us conscious and subconscious. And the latter gives us the ability to not make the same choice over and over again because making a choice is exhausting. Imagine your life when every morning you need to decide if you are going to brush your teeth today or no.&lt;/p&gt;
&lt;h3 id=&quot;integrity-is-trust&quot;&gt;Integrity is trust.&lt;/h3&gt;
&lt;p&gt;When you promise to do something, and you fail, you lose a little bit of integrity. When this happens another time, you lose a bit more. You lose some more for the third time as well. But after that, you lose trust. You are no longer a person others can rely on or expect help from.&lt;/p&gt;
&lt;p&gt;It’s a bad position to be in. Humans, as species, survived because we were able to group and divide tasks between group members. If you are invaluable to the group or the group can not trust you, why would they take you with them? And yes, you can ramble that you prefer to be friends with people who don’t need anything from you but think about this for a moment. Imagine your self in a situation when you are having a big argument with your family or your SO and desperately need a place to sleep in. Who would you go to? Now can you be that person for some of your friends?&lt;/p&gt;
&lt;p&gt;And yes, you can have friends without doing or asking for favors. But you will never have a deep connection with people. Deep connection relies on giving and asking for help. And you can’t have a deep connection without having integrity, because trustworthy people have integrity.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Integrity is everything. All other qualities are based on integrity and that’s the reason integrity is the most important quality.&lt;/p&gt;</content:encoded></item><item><title>3 Things I&apos;ve learned by breaking my phone</title><link>https://kudmitry.com/articles/3-things-ive-learned-by-breaking-my-phone/</link><guid isPermaLink="true">https://kudmitry.com/articles/3-things-ive-learned-by-breaking-my-phone/</guid><pubDate>Sun, 21 Jul 2019 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About a week ago I’ve visited the gym as usual and took my phone with me. During one of the exercises, I’ve missed the cage hook for the barbel and the barbell fell onto the phone and broke the screen. The screen was 2/3 dead and the reaming 1/3 died in the following hours. I should have probably been sad, mad at my self, mad at the world, the phone manufacturer and what not. But instead, I’ve decided to see what could happen if I try to learn something from it.&lt;/p&gt;
&lt;h2 id=&quot;1-bad-things-happen-be-prepared&quot;&gt;1. Bad things happen, be prepared.&lt;/h2&gt;
&lt;p&gt;My old phone was very good. Even though I dropped it a few times and I suspect that the speaker was damaged somehow, I did not plan to replace the phone any time soon. But then it was damaged beyond repair and as sad as it might sound – a smartphone is a mandatory gadget in our time. It allows you to be online everywhere – be responsive to work and personal life events, authenticates via 2-step authentication at most websites and of course make calls.&lt;/p&gt;
&lt;p&gt;A good phone costs a significant amount. And I did not plan to spend that amount right now. My fiancée and I just moved into a new apartment, we had to furnish it. We are having a wedding soon followed by a honeymoon. All this requires financial expenses. And if I didn’t have an emergency fund or extra money lying around – I’d probably be in a tight financial situation now.&lt;/p&gt;
&lt;p&gt;Bad things happen all the time. And breaking a phone is the least of all. You could get sick. You could lose your job. Someone from your family or friends might need financial help. If we live like there is no tomorrow, and we do not save any money aside – when bad things happen, we will be in a bad situation.&lt;/p&gt;
&lt;p&gt;And it’s not limited only to financial matters. You must have backup strategies for all emergencies. For example – how you can notify your family members that your phone is dead, and you won’t be able to take calls in the following 12–24 hours until you get a new phone? How are you going to authenticate with your email provider without having access to your 2-step authentication codes? How are you going to pay if you lose your credit card, or it gets compromised?&lt;/p&gt;
&lt;h2 id=&quot;2-a-fresh-start-to-break-the-status-quo&quot;&gt;2. A fresh start to break the status quo.&lt;/h2&gt;
&lt;p&gt;Humans are used to the status quo. It’s hard to make a change when everything is working, even if it’s not working 100% correct. By getting a new phone I had 0 apps installed in it. And I decided to myself that I’m going to install an app only when I need it. I still haven’t installed Instagram (the only social network app I’ve had previously) and not yet sure I’m going to install it. I also got rid of much of the apps I never used but was hesitant to delete because “I might need them one day”.&lt;/p&gt;
&lt;p&gt;I believe that order is required in all aspects of life and having your phone cluttered with a zillion of apps you never use, contributes to the cultural ADHD we have as a society.&lt;/p&gt;
&lt;h2 id=&quot;3-it-is-your-fault&quot;&gt;3. It is your fault.&lt;/h2&gt;
&lt;p&gt;I never broke a phone before. I am relatively careful with my electronics. A chain of events that ended with me not being careful brought this situation on me.&lt;/p&gt;
&lt;p&gt;I saw people who miss a bus and the driver won’t let them in on the intersection. I saw how they get furious, how they call the driver bad names. I saw people who drop their phones and blame the entire world that made them drop their phones.&lt;/p&gt;
&lt;p&gt;I could have said that this stupid gym does not have a proper place to put phones. I could have blamed the gym. But instead, I blame myself. I blame myself for taking a phone into the gym. I blame myself for not being careful with the barbell and for putting the phone in a “hot zone”.&lt;/p&gt;
&lt;p&gt;Responsibility is something that few people have. They tend to blame everyone in their problems, except for them. &lt;em&gt;“No, there is no chance that I’m the problematic one”&lt;/em&gt;. Well, guess what – you are responsible for 99% of the things that happen to you. The sooner you understand it, the better for you.&lt;/p&gt;
&lt;p&gt;And in case anyone wondering, no I do not advise you to break your phone. I’m just sharing with you a different way to look at problematic situations in life when and after they happen.&lt;/p&gt;</content:encoded></item><item><title>You Are Not Your News Feed! Or maybe you are?</title><link>https://kudmitry.com/articles/you-are-not-your-news-feed-or-maybe-you-are/</link><guid isPermaLink="true">https://kudmitry.com/articles/you-are-not-your-news-feed-or-maybe-you-are/</guid><pubDate>Sun, 07 Jul 2019 09:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;You are the average of the five people you spend the most time with – Jim Rohn&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Back then, you’ve spent most of your time with your friends from school. Your social circle was limited to 20-30 other children that were together with you in class. There was no internet, at least not in the form we know it today. The amount of information you were exposed to was very small compared to today.&lt;/p&gt;
&lt;h2 id=&quot;in-the-beginning-god-created-the-heaven-and-the-earth-then-he-posted-about-it-on-facebook&quot;&gt;In the beginning, God created the Heaven and the Earth. Then he posted about it on Facebook.&lt;/h2&gt;
&lt;p&gt;Your day probably starts with an alarm clock and an Instagram feed. Some of my days start like this, even though I try to get rid of both the alarm clock and the morning Instagram ritual. Then your day probably includes breakfast with newspaper (if you are from the seventies) or yet another social feed. Then a car and the same song from yesterday on the radio, the lyrics of which you subconsciously know.&lt;/p&gt;
&lt;h2 id=&quot;we-use-only-10-of-our-brains&quot;&gt;We use only 10% of our brains.&lt;/h2&gt;
&lt;p&gt;This subtitle is actually misleading. Scientific experiments proved that we use 100% of our brain because every part of the brain emits electronic impulses.&lt;/p&gt;
&lt;p&gt;On my last birthday, my fiancée made a surprise for me, and apart from arranging breakfast with her and my family and dinner with my friends, she also booked me a sports car driving session. The experience itself was amazing but for the context of this post, I’d like to outline one specific thing I’ve learned from the instructor.&lt;/p&gt;
&lt;p&gt;He told me something like:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;You see, most drivers focus on the car in front of them, they try to follow it and predict what will happen on the road, based on this car. It slows down, you slow down. It speeds up, you speed up. But this is &lt;em&gt;looking one step ahead&lt;/em&gt; approach. The amount of information you have about the road and driving conditions based on the car in front of you. Professional drivers, however, need to think more than one step ahead. They need to plan their speed before the road turns or goes in curves. And by focusing on the car in front of them, they lose a lot of environment information. What they do instead is focus on the furthest point on the road.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I was stumbled. “How is it possible? If you focus on the furthest point on the road, don’t you miss everything that is in front of you?” – I’ve asked. “No” – he said. “We actually underestimate the ability of our brain and peripheral vision. Try to focus on the furthest point in the road and try to predict where the road is going, where are the turns and how sharp they are. And you will see, that subconsciously you won’t miss any information.”&lt;/p&gt;
&lt;p&gt;I tried. At first, it was scary and uncomfortable. But after a few minutes, I got it. I understood that even if you focus consciously on the furthest point on the road, your peripheral vision grasps the entire road conditions.&lt;/p&gt;
&lt;h2 id=&quot;everything-matter-even-if-you-think-its-not&quot;&gt;Everything matter even if you think it’s not.&lt;/h2&gt;
&lt;p&gt;In today’s modern world, we are no longer an average of 5 people. We are the average of our social feeds, the music we listen to and the people who surround us.&lt;/p&gt;
&lt;p&gt;Your Instagram feed matters. What you see there – shapes who you are. The music you listen to, even if you think you don’t hear the words – they matter. A singer that sings about how all men are assholes – will eventually shape girls minds to think that all men are assholes. The Instagram feed of a guy that laughs at women – will eventually shape boys minds into disrespecting women.&lt;/p&gt;
&lt;p&gt;We think that those are just pictures or words – but they have power. People who read the news, tend to be more paranoid and think that the world is a terrible place. Everything we see or hear, have power. With great power – comes great responsibility. And it’s our responsibility to filter the information we want to be exposed to.&lt;/p&gt;
&lt;h2 id=&quot;same-jeans-same-t-shirt&quot;&gt;Same jeans, same t-shirt.&lt;/h2&gt;
&lt;p&gt;In my twenties I was depressed. One of the ways my depression showed itself was that I didn’t really care about how I dress and look. Don’t get me wrong, I showered and brushed my teeth constantly, but I wore the same jeans, same t-shirt, and same sneakers every day and replaced the sneakers only when they got a big hole in them.&lt;/p&gt;
&lt;p&gt;One day I decided – no more. I want to look sharp. At first, I tried to put on dress shoes and button-up shirts, but it looked funny, and I felt ridiculous in it. The whole process of adjusting my outfit took a good several years, and eventually I’ve started to get compliments that I dress well from my family, friends, friends of friends and even work colleagues.&lt;/p&gt;
&lt;p&gt;You probably think now that I understand how color matching works, that I shop at brand stores and have a personal outfit consultant. But no, it’s way simpler than that. My Instagram feed is filled with accounts of so-called “man fashion”. Those are people who make money promoting different outfits or just guys that dress well. And the only thing I know consciously about fashion is that matching 2 colors is good. Everything else is my subconsciousness that was exposed to good information from Instagram.&lt;/p&gt;
&lt;h2 id=&quot;information-and-responsibility&quot;&gt;Information and responsibility.&lt;/h2&gt;
&lt;p&gt;Our daily habits of checking the news, following stupid Instagram accounts or listening to music that disrespect men or women – might look like something minor. “Oh come’on, I don’t even listen to the words!” – you can say. But I say, maybe you don’t – but your brain does. And we might think of ourselves as smart and sophisticated creatures, and we are indeed, but somewhere, deep inside, we are no different from a dog. You tell it “sit” once, and it ignores you. You tell it “sit” twice, it will be confused but will sit. You tell it “sit” three times and it will sit.&lt;/p&gt;
&lt;p&gt;The same thing happens to us. There is no harm in reading the news once a month. But on a constant basis, it will rewire your brain. It will teach it to look for the negative in the world, seeing only the terrible things that happen in the world. But the world is not terrible. Yes, there are terrible things happening in the world. But there are also good things happening. There is a lot of joy and happiness in the world. And the news, they do not reflect that. News is not the source of good things.&lt;/p&gt;
&lt;p&gt;But then, we go to the gym every few days and do exercises with unimportant weights. We read 1 book per month that teaches us something new. We play a few notes every day on the guitar. And eventually – our muscles will grow. Our intelligence will rise. Our musical skills will perfect.&lt;/p&gt;
&lt;p&gt;We are who we are because of the things we do (and don’t do) over time. Skill, intelligence, strengths – they all products of small, unimportant actions multiplied by time.&lt;/p&gt;
&lt;p&gt;And guess what? It works both ways. Going to the gym will build muscle – eating junk food will build fat and disease. Reading deep books that challenge our beliefs will build intelligence – reading the news will build fear. Listening to podcasts will widen our knowledge – listening to hateful music will build hate in us.&lt;/p&gt;
&lt;p&gt;Think about it the next time you are exposed to information.&lt;/p&gt;</content:encoded></item><item><title>Do It Yourself and Self Improvement</title><link>https://kudmitry.com/articles/do-it-yourself-and-self-improvement/</link><guid isPermaLink="true">https://kudmitry.com/articles/do-it-yourself-and-self-improvement/</guid><pubDate>Sun, 23 Jun 2019 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do It Yourself – is a phrase that today reserved for makers. The rest of us value “throw away” culture. But DIY approach has a lot of psychological benefits.&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;After moving out from my parents house, to live by myself, I’ve had only a laptop. I’ve used this laptop for the entire year at my first rental flat. After moving to a bigger apartment, I’ve decided to treat myself with a new PC. As a maximalist, I want the best I could get for my money, and a keyboard was not overlooked.&lt;/p&gt;
&lt;p&gt;I opened for myself the world of mechanical keyboards at that time and I said that I want one. I won’t go into deep details about the difference between membrane keyboard and a mechanical one, but in a nutshell – mechanical keyboards give you a better feeling when typing and have a longer life span than a regular membrane keyboard. And while I can argue that gold-plated 100% oxygen free copper speaker cables, have no objective difference compared to good quality, speaker cable; mechanical keyboards are actually superior to membrane or scissors keyboards, especially if you are a heavy typist such as software engineer or a writer. I know it, I’ve used all 3 types.&lt;/p&gt;
&lt;p&gt;Anyway back to the story. I got myself the best mechanical keyboard I could afford. For the next 7 or so years, my mind was occupied with either work or yet another obsessive hobby such as photography. I forgot all about that keyboard. Up until the day my mind once again became obsessed that I need a different keyboard now. One which is smaller, more ergonomic.&lt;/p&gt;
&lt;h2 id=&quot;we-live-in-communities&quot;&gt;&lt;strong&gt;We live in communities&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I, of course, started to look for the best I can get. One thing I’ve learned: there is a community around everything. And while mechanical keyboards are not that popular as a hobby compared to, let say mountain biking, apparently, there is an entire market of custom mechanical keyboards.&lt;/p&gt;
&lt;p&gt;And the scale of customizability ranges from making different keycap colors (keycap – is the individual plastic cap of your keyboard, let’s say the cap for the letter “Y”) all the way towards designing custom PCB (PCB – Printed Circuit Board, an electronic board that makes your electronics work). And I was debating between ordering a pre-made keyboard from a known manufacturer or buying all the parts and actually making the keyboard myself. Price wasn’t a factor as the difference was negligible.&lt;/p&gt;
&lt;h2 id=&quot;diy--do-it-yourself&quot;&gt;&lt;strong&gt;DIY – Do It Yourself&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;When I buy a product of quality, I expect that the company that makes this product to have employees that know what they are doing. I also expect them to have better quality control and a polished assembly line. That’s the reason I buy products that are already assembled and tested, rather than making them myself. I simply lack the tools, knowledge, manpower and time to make everything myself.&lt;/p&gt;
&lt;p&gt;Also, when you make something yourself, it’s always imperfect, especially if you make it for the first time. You do not have the necessary equipment and tools to make it; you lack the knowledge to make it, etc. But there is also an amazing satisfaction feeling in making something yourself. My fiancée and I made a wooden shelf some time ago. It’s far from perfect, but it’s ours, and it decorates our bedroom in an exceptional way. Apart from this, the whole process of making it was captivating for both of us. We got to spend time together in a non-regular way; we got to make something and use equipment we didn’t use before, and we got into a situation of teamwork that is different from the day-to-day situations you get to.&lt;/p&gt;
&lt;p&gt;So as you guessed, I’ve decided to make my own keyboard. There is a scale on how hardcore would you like to go in a DIY project starting from getting a ready board that requires only assembling all the way to making your own PCB or even keycaps. I took the middle ground and ordered a ready PCB, switches, keycaps and some other parts – and soldered my own keyboard. I type that post from this very keyboard I’ve made.&lt;/p&gt;
&lt;h2 id=&quot;its-not-perfect-but-its-mine&quot;&gt;&lt;strong&gt;It’s not perfect but it’s mine&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;It’s far from perfect. Some keys are not aligned, and it was my first major soldering project, so I screwed some soldering joints. Am I happy? Yes, of course. Using something you’ve made is a great satisfaction. It also taught me a lot of things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It taught me that everything is possible. We live in a very fortunate time, a time when the entire knowledge of the human race is accessible with a few keystrokes and internet. You no longer need to attend school or college to learn how to solder or make furniture. There are a lot of free and paid tutorials, books and videos about every possible topic. And while some disciplines such as doctors or certified mechanics, requires attending a certification program or school, your small DIY project will be fine if you’ve learned how to solder from a YouTube video.&lt;/li&gt;
&lt;li&gt;It gave me the courage to try other projects in the future. It gave me confidence that if something goes bad I’ll try to fix it instead of calling a professional to fix it or worse – throw it away. It’s a very valuable skill – the ability to know that you are in control. Our generation used to throw things away. Our phone doesn’t work – we throw it away. Table got scratched – we throw it away. Our relationship doesn’t work – we throw it away. And while sometimes throwing things away because they are broken beyond repair – is the correct solution, having the courage and the ability to fix them, or at least try to, teaches us a lot more than just fixing our phone and saving money – it teaches us that a good and happy life requires hard work rather than always taking the path of least resistance.&lt;/li&gt;
&lt;li&gt;It helped me fight with my perfectionism. I do believe that being a perfectionist is not bad by itself, but it should be moderated. If you see something that is a little bit misaligned, and you feel that part of you dies – this is a bad feeling to have. The world is imperfect and it’s fine. In Japan, they have a worldview called &lt;em&gt;Wabi-sabi&lt;/em&gt; which is centered on the acceptance of imperfections. It’s a concept derived from Buddhism. Striving to perfection is fine – but it’s also very important to accept imperfections.&lt;/li&gt;
&lt;li&gt;It showed me how things work. I don’t know how it works in other places in the world, but the country I live in requires mandatory knowledge of how a car works to get your driving license. And while this knowledge is limited to basics only, without going into details of how an internal combustion engine works or what is compression ratio, this is still mandatory. And no, you can not know everything. The world has a lot of things and some of them are very complicated. But having basic knowledge about the things that surround us or that we use daily – is valuable common knowledge that won’t be useless.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-can-you-do&quot;&gt;&lt;strong&gt;What can you do?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;You can start small. Assemble IKEA furniture – it’s a great satisfaction watching TV that stands on a TV stand that you’ve assembled.&lt;/p&gt;
&lt;p&gt;Don’t be afraid to make mistakes. I’ll tell you even more – you will make mistakes, and it won’t be perfect and it’s fine. We, humans, have this bias that if something comes out of a factory – it’s way better than if we would assemble it ourselves. But think about it this way: the guy who assembled your keyboard, have to assemble tens of such per day, and he gets paid not by quality but by quantity. His interest is not doing it perfectly but rather doing it “good enough” and I bet you can do it “good enough” as well and apart from saving money – you will also gain satisfaction.&lt;/p&gt;
&lt;p&gt;So don’t hesitate to try, even if you will fail, and you will, you will also get a lot of benefits from it.&lt;/p&gt;</content:encoded></item><item><title>Context Switch - The only operation that kills productivity</title><link>https://kudmitry.com/articles/context-switch-the-only-operation-that-kills-productivity/</link><guid isPermaLink="true">https://kudmitry.com/articles/context-switch-the-only-operation-that-kills-productivity/</guid><pubDate>Fri, 26 Apr 2019 09:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;In computing, a context switch is the process of storing the state of a process or of a thread, so that it can be restored and execution resumed from the same point later. This allows multiple processes to share a single CPU, and is an essential feature of a multitasking operating system.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;— &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Context_switch&quot;&gt;Wikipedia&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;See, a single core CPU can not multitask. It is all a lie. What really happens when single core CPU multitasks, is that it runs small parts of each program for a small period of time. The higher the &lt;a href=&quot;https://en.wikipedia.org/wiki/Instructions_per_second&quot;&gt;IPS&lt;/a&gt; of the CPU, the more “multitasking” the CPU looks.&lt;/p&gt;
&lt;p&gt;But in order to “multitask”, the OS needs to perform one very expensive operation — Context Switch. What happens in that operation is that it needs to store all the data of the current running program: its CPU registers and flags. Then unload the current program from the CPU, load the next program to the CPU, load its CPU registers and flags, and let it run for some small amount of milliseconds (somewhere around 100, depends on the OS scheduler implementation), until the OS decided to perform another context switch.&lt;/p&gt;
&lt;p&gt;This is probably the most interesting and critical part of designing an operation system. If your context switching algorithm sucks, your OS becomes irresponsive, slow and eventually hangs.&lt;/p&gt;
&lt;h2 id=&quot;we-are-like-single-core-cpus&quot;&gt;We are like single core CPUs&lt;/h2&gt;
&lt;p&gt;Humans can not multitask. The brain is created to perform one task at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It looks like&lt;/strong&gt; you multitask sometimes, but what actually happens is that you, like an OS, perform context switch. And as in OS, your context switch is a very expensive operation.&lt;/p&gt;
&lt;p&gt;Try to answer a phone call during intensive, brain required, work. Chances are you will forget most of the content of the call, because it takes your brain time to leave what it was doing (thinking and analyzing) and switch to another task (listening and remembering), not to mention that once the call is over, it will take you on average &lt;strong&gt;25 minutes&lt;/strong&gt; (&lt;a href=&quot;https://www.ics.uci.edu/~gmark/CHI2005.pdf&quot;&gt;No Task Left Behind? Examining the Nature of Fragmented Work&lt;/a&gt;) to go back to the task you were doing.&lt;/p&gt;
&lt;p&gt;Some people are better at it (especially women), some are less, but for everyone — context switch is &lt;strong&gt;the&lt;/strong&gt; productivity killer. It doesn’t matter if you wake up at 4am every day, eat healthy, do sports and read books — if you do a lot of context switching during the day, you won’t be as productive as you could be.&lt;/p&gt;
&lt;p&gt;And it seems like no one in the modern world understands it.&lt;/p&gt;
&lt;p&gt;Instant messaging, annoying notification pop ups, frequent meetings, open space work environment — all those and others encourage context switching. And thus — reduces productivity.&lt;/p&gt;
&lt;h3 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h3&gt;
&lt;p&gt;As someone who needs his full brain power, you need to tweak your working environment towards less context switching. Disable most of the notification — honestly they are not that important most of the times. Put your phone on silent; disable notification pulse lights and sounds; use headphones with relaxing, instrumental music; avoid meetings; practice concentration through meditation.&lt;/p&gt;
&lt;p&gt;And avoid instant messaging — not only it distracts you from your tasks, but it is not an effective way for communication. It is great for things like “&lt;em&gt;see you tomorrow at 6pm on main street 1 cafe&lt;/em&gt;”, but its awful tool for &lt;strong&gt;real&lt;/strong&gt; conversations. You never have the full brain capacity to read, analyse, sympathize and respond correctly. In order to actually do all this, you need to dedicate time to real communication as opposed to communication “by-the-way”. This is also true for offline communication — never go watch a movie if what you want is to communicate, instead prefer a walk or a dinner with drinks; those are better for communication rather than the constant context switch between the movie and your partner / friends / family.&lt;/p&gt;
&lt;p&gt;And if you are a manager who looks to increase the productivity of your team — promote quite times; drop those useless meetings or schedule a meetings day — a day that is dedicated entirely to meetings; prioritize — not every small email is important &lt;strong&gt;right now&lt;/strong&gt;; promote documentation of your team’s members work, so team members wont spend time asking one another about parts of team’s work they were not involved in; promote non peer to peer communication — avoid IMs, prefer issue tracker that can be handled when the person has time to.&lt;/p&gt;
&lt;h3 id=&quot;the-key-is-flow&quot;&gt;The key is FLOW&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;In positive psychology, flow, also known as the zone, is the mental state of operation in which a person performing an activity is fully immersed in a feeling of energized focus, full involvement, and enjoyment in the process of the activity. In essence, flow is characterized by complete absorption in what one does, and a resulting loss in one’s sense of space and time. — &lt;a href=&quot;https://en.wikipedia.org/wiki/Flow_(psychology)&quot;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Flow is the new trend today. Be in the flow. Whether its work flow, conversation flow, listening flow, watching flow, reading flow. Your brain is not capable of doing multiple things at once, it performs best when its in a flow state. Embrace the flow. There is a reason why people experienced flow state describe it as the most productive work they’ve done. It is because in the flow you have only the sense of the task, you do not spend any energy thinking about or concentrating on different tasks or areas.&lt;/p&gt;
&lt;h3 id=&quot;multitasking-is-not-the-answer&quot;&gt;Multitasking is not the answer&lt;/h3&gt;
&lt;p&gt;We keep hearing a lot about how multitasking is important; the world has gone crazy and we need to do a lot of stuff in order to succeed. “&lt;em&gt;Being able to switch between different tasks fast&lt;/em&gt;”, “&lt;em&gt;Can work in a fast moving environment&lt;/em&gt;” — those are one of the common job descriptions you can find today, and the ones you need to avoid.&lt;/p&gt;
&lt;p&gt;The only way to succeed is to devote your full attention to the particular activity, whether its work, communication, reading, doing sports, meditating. Really successful people know how to plan — plan work, plan leisure, plan everything. And they know how to disconnect — leave your work behind when you spend time with family; leave your trip planning behind when you work and etc.&lt;/p&gt;
&lt;p&gt;Avoid multitasking; avoid being &lt;em&gt;flexible&lt;/em&gt;; avoid working in a &lt;em&gt;fast moving environment&lt;/em&gt;. You will not succeed there. You might feel like you are, but what actually will happen is that you will break down. Break down because multitasking does not work. The only thing that multitasking does to you (apart for the health problems it is associated with such as: brain harm, memory problems, increased distractibility, chronic stress, depression and social anxiety)— it makes you do mediocre work in more time; makes you spend your potential on stress; makes your brain keeps wasting its power as well as your energy, to a very expensive operation — Context Switching.&lt;/p&gt;</content:encoded></item><item><title>React.JS Localization v0.2</title><link>https://kudmitry.com/articles/react-js-localization-v0-2/</link><guid isPermaLink="true">https://kudmitry.com/articles/react-js-localization-v0-2/</guid><pubDate>Mon, 24 Jul 2017 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Remember my &lt;a class=&quot;internal new&quot; href=&quot;/articles/a-different-approach-for-localizing-react-js-app/&quot;&gt;last post&lt;/a&gt; about React.js localization? Not 2 years passed yet and I’m happy to share with you the “proper” way to do localization in React.js.&lt;/p&gt;
&lt;h2 id=&quot;globalizejs&quot;&gt;Globalize.js&lt;/h2&gt;
&lt;p&gt;Yes, I still use &lt;a href=&quot;https://github.com/globalizejs/globalize&quot;&gt;globalize&lt;/a&gt;. As I said in my previous post, the Common Locale Data Repository is maintained by Unicode Consortium and have support for every possible locale. Please don’t re-invent the wheel. Languages are hard (believe me, I know Russian) and I’ve seen a lot of people trying to do their own localization and plurals. Kudos to them, its a valuable experience to get, but when you are doing a professional web site, you want to use proper plurals.&lt;/p&gt;
&lt;p&gt;But while CLDR is only data (usually JSON, but if you are old and nostalgic you can get it in XML format), globalize is the tool. You feed globalize with data and it gives you way to produce shiny localized numbers or dates. Globalize API is very extensive and &lt;strong&gt;modular&lt;/strong&gt;, the core library is only 1KB minified + gzipped and you can add other modules depending on your needs.&lt;/p&gt;
&lt;h2 id=&quot;getting-cldr&quot;&gt;Getting CLDR&lt;/h2&gt;
&lt;p&gt;The way you get CLDR did not change from my previous post. I still think it should be part of your build step. The only difference is that I no longer use &lt;a href=&quot;https://www.npmjs.com/package/cldr-data-downloader&quot;&gt;cldr-data-downloader&lt;/a&gt; since it is limited in the way it downloads the CLDR. Instead I use the combination of gulp-download and gulp-decompress as well as a custom mapping JSON file to map the different packages and versions of CLDR.&lt;/p&gt;
&lt;p&gt;What I do recommend you pay attention to, is that CLDR package is split into two “modules”: &lt;strong&gt;Locale&lt;/strong&gt; files and &lt;strong&gt;Supplemental&lt;/strong&gt; files. It is important that you do not ship everything as one json file. Supplemental data is shared across all locales, so if a user switches between different locales, he will use the same supplemental file, but different locale file. This will allow the browser to cache the supplemental.json file if it was already requested once. So while you package your cldr, split it into at least 2 files: &lt;code&gt;{locale}.json&lt;/code&gt; and &lt;code&gt;supplemental.json&lt;/code&gt;, where &lt;code&gt;{locale}&lt;/code&gt; is being the locale you are shipping.&lt;/p&gt;
&lt;p&gt;Here are the 2 gulp tasks that I use to create &lt;code&gt;supplemental.json&lt;/code&gt; and &lt;code&gt;{locale}.json&lt;/code&gt; for the locales I support:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:supplemental&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:clean&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gulp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      CLDRConfig&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;files&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_SOURCE_PATH&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;supplemental&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;supplemental.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_DEST_PATH&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:locale&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:clean&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; streams&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;i18n&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locales&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gulp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        CLDRConfig&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;files&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;          return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_SOURCE_PATH&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_DEST_PATH&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; merge&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;streams&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;localization&quot;&gt;Localization&lt;/h2&gt;
&lt;p&gt;Now that you have your CLDR ready, you can start with the actual localization.&lt;/p&gt;
&lt;p&gt;The main and the most important file is the GlobalizeProvider:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; PropTypes&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;prop-types&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Globalize&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;globalize&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; provide&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  Globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  Globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; globalize&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; GlobalizeProvider&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; childContextTypes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    getChildContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; GlobalizeProvider&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;locale — is the actual locale name (e.g. “en”, “ru” etc), supplemental — is the content of supplemental.json and data is the content of &lt;code&gt;{locale}.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can theoretically load ALL locale data you will ever support, but its a huge huge huge waste. Locale is not something that users change every 5 minutes, so if I prefer an &lt;strong&gt;en_US&lt;/strong&gt; version of you website, most likely Ill never switch to &lt;strong&gt;ru&lt;/strong&gt; or &lt;strong&gt;en_GB&lt;/strong&gt;, and even if I will switch, I’m ok with you reloading the website and loading the locale data again.&lt;/p&gt;
&lt;p&gt;So we have the GlobalizeProvider. It uses context to pass the globalize instance (you can read about context in &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-land-of-undocumented-react-js-the-context/&quot;&gt;one of my posts&lt;/a&gt;). We will need one more Container component that handles the locale loading based on any strategy you want (guess it from the browser, headers, make the user select; whatever, I prefer to start with en_US and let the user select). Here is a snippet of my RootContainer component that handles the locale loading (I’m using redux here):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react-redux&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;BrowserRouter&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Switch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react-router-dom&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Actions&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; LocaleActions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../actions/locale&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; GlobalizeProvider&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../globalize/Provider&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; ErrorComponent&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../components/Display/Error&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; LoadingComponent&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../components/Display/Loading&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../../styles/style.sass&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Root&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  componentWillMount&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      dispatch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocaleActions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;loadSupplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      dispatch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocaleActions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;loadLocale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ErrorComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;Failed to load locale data&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ErrorComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LoadingComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; GlobalizeProviderContainer&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; GlobalizeProvider&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;GlobalizeProviderContainer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SignNDAtoViewThisComponent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;GlobalizeProviderContainer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; select&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; connect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Root&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we have GlobalizeProviderContainer that wraps our whole application, and since it uses context we can pass initialized globalize instance to every leaf component.&lt;/p&gt;
&lt;h2 id=&quot;localized-leaf-components&quot;&gt;Localized Leaf Components&lt;/h2&gt;
&lt;p&gt;The only thing left is to implement Localized leaf components.&lt;/p&gt;
&lt;p&gt;Here is an example for DateTime component to display date &amp;#x26; time in given format:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; PropTypes&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;prop-types&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; DateTime&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; propTypes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    date&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;instanceOf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; contextTypes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; _formatters&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; DateTime&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_formatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      DateTime&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_formatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dateFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;skeleton&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; DateTime&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;_formatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; formatter&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; DateTime&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      formattedDate&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; formatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component localized datetime&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;formattedDate&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the &lt;em&gt;static contextTypes&lt;/em&gt; declaration. This is what gives us access to the globalize instance provided from GlobalizeProviderContainer (for more info read my &lt;a class=&quot;internal new&quot; href=&quot;/articles/the-land-of-undocumented-react-js-the-context/&quot;&gt;context post&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Inside the Localized component, you use pure globalize API to access the different formatting methods.&lt;/p&gt;
&lt;p&gt;You can even query the CLDR itself for data. Take a look at Currency component below:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; PropTypes&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;prop-types&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Currencies&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;../../../../data/currencies.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Currency&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; contextTypes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; propTypes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    display&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;oneOf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;symbol&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    currency&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;oneOf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;Currencies&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  static&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; defaultProps&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    switch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      case&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component currency name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;            {&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cldr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;              `numbers/currencies/&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currency&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;/displayName`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            )&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        break&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      case&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;symbol&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component currency symbol&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currency&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        break&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      default&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component, when rendered like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Currency&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; currency&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; display&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;symbol&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Will produce:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component currency symbol&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;USD&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But when rendered like this&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Currency&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; currency&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; display&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Will produce&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component currency name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;US Dollar&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For &lt;em&gt;en_US&lt;/em&gt; locale, and for the &lt;em&gt;ru&lt;/em&gt; locale it will produce&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;component currency name&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;Доллар США&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now go on and implement as much Localized Leaf components as you wish, depending on your needs. I have Country and Currency Selects that are entirely localized, Date Picker that is based entirely on CLDR and Globalize (as opposed to a lot of date pickers that use internal localization files) and etc.&lt;/p&gt;
&lt;p&gt;Hope this helped you to become a better React.js developer. Best of luck :)&lt;/p&gt;</content:encoded></item><item><title>Validating Requests With a Simple Middleware for Express</title><link>https://kudmitry.com/articles/validating-requests-with-a-simple-middleware-for-express/</link><guid isPermaLink="true">https://kudmitry.com/articles/validating-requests-with-a-simple-middleware-for-express/</guid><pubDate>Sat, 27 Aug 2016 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A key to a good API design is good control of what data your controllers or API end points accepts. Another important key to a good design — is that you can take a brief look at your API end point and immediately understand how your request should look.&lt;/p&gt;
&lt;p&gt;Now, of course you can validate all the request parameters inside the API end point itself. But this makes your end point messy and sort of violates the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_responsibility_principle&quot;&gt;SRP&lt;/a&gt; (your end point should not validate requests. It should only work with them and provide a response). Now, if you are old fashioned and work with XML requests / response, you can of course use XSD to validate your incoming requests. But we are in 2016 where JavaScript runs in your browser, your desktop environment, your media center, you smart watch, your smart home and soon in your brain implants. So we gonna use JSON!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But, how do I validate JSON?&lt;/em&gt; I’m glad you’ve asked. Meet &lt;a href=&quot;https://github.com/hapijs/joi&quot;&gt;joi&lt;/a&gt; by &lt;a href=&quot;http://hapijs.com/&quot;&gt;hapijs&lt;/a&gt;. This small and awesome library can validate your JavaScript objects. It can do all kind of stuff like making sure that a specific key has only specific set of values, or that specific key is only required if another key exists and contains a specific value. So as always, RTFM.&lt;/p&gt;
&lt;p&gt;We now have joi to validate our JSONs. But we still need to find an elegant way to put it inside the life cycle of our specific API end point. I’m too lazy to create a specific, generic NPM module so I’ve just dumped the current code I use in my project to a &lt;a href=&quot;https://gist.github.com/skwee357/7e4ca3b913f4b1202f51d72fcb1ac0db&quot;&gt;gist&lt;/a&gt;. Feel free to use it or create a proper NPM module (don’t forget to mention me for being your inspiration ;)&lt;/p&gt;
&lt;p&gt;The middleware is very simple. It defines a Bad Request Error that will be thrown if the incoming request is malformed, and what it does, it simply validate the request against a given &lt;em&gt;schema&lt;/em&gt; (a bit later on that) and assigns the validated object (essentially this is &lt;em&gt;req.body&lt;/em&gt; but with type conversions and modifiers applied, more on this in &lt;a href=&quot;https://github.com/hapijs/joi/blob/v9.0.4/API.md#validatevalue-schema-options-callback&quot;&gt;joi API documentation&lt;/a&gt;) to the req.schema (not the best name) so it can be accessed later on inside the API end point (or any next middleware in the chain).&lt;/p&gt;
&lt;p&gt;Now lets talk about the schema and how to actually use this. Schema is just a JSON with joi rules. Lets say we have 2 API end points: One for user creation and one for user update. We define then 2 schema:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  create&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    username&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;required&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    password&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;required&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;required&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  update&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    username&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Joi&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;optional&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to create a new user we need to provide 3 required keys: username (at least 3 characters), password (at least 3 characters) and email (should be a real email). And in order to update a user we can just provide an optional new username (at least 3 characters) (side note: an empty request in such case is a valid request, it is up to your end point to make sure to do no-op if the request is empty). We then define our API end points like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; SchemaValidationMiddleware&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;path/to/schema/middleware&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  UserSchema&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;path/to/schema/user&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  UserController&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;path/to/user/controller&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &quot;/users&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  SchemaValidationMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;validate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;UserSchema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  UserController&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;createNew&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;patch&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;  &quot;/users/:id&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  SchemaValidationMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;validate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;UserSchema&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;update&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  UserController&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;edit&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And inside the controller / API end point we can use &lt;em&gt;req.schema&lt;/em&gt; to access the validated schema and perform the required operations. And we can be sure that the controller / end point will be executed only if our request meets the schema we defined for it. The &lt;em&gt;UserController.createNew&lt;/em&gt; will never be called if one of the keys we defined as required in our schema — missing, if they are mistyped, if there are keys that we are not expecting and etc.,&lt;/p&gt;
&lt;p&gt;This gives the answer to the 2 design keys of a good API that I’ve outlined in the beginning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Controllers / end points only manipulates the request and provide a response. They are not used to validate the request. We can be sure that our controllers / end points will never be called with an invalid request.&lt;/li&gt;
&lt;li&gt;By looking at our &lt;em&gt;schema/user.js&lt;/em&gt; we can immediately understand how our request should look. We also can give those files to our customers / put them in our API documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can extend this idea and provide validation for responses as well. By writing a joi schema for the response and adding another middleware after your actual API end point that will validate the response you want to send against a response schema, so this can help you to avoid cases where developers might mistype a response key for examples &lt;em&gt;usrename&lt;/em&gt; instead of &lt;em&gt;username&lt;/em&gt;. And also by having request and response schema — your API is essentially self documented.&lt;/p&gt;
&lt;p&gt;Hope this helped you to become a better JavaScript Jedi! Please share and like! Leave a comment or hit me on &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;Twitter&lt;/a&gt; if you have any questions.&lt;/p&gt;</content:encoded></item><item><title>The land of undocument react.js: The Context</title><link>https://kudmitry.com/articles/the-land-of-undocumented-react-js-the-context/</link><guid isPermaLink="true">https://kudmitry.com/articles/the-land-of-undocumented-react-js-the-context/</guid><pubDate>Thu, 03 Sep 2015 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Русская версия на&lt;/em&gt; &lt;a href=&quot;http://habrahabr.ru/post/266407/&quot;&gt;&lt;em&gt;Хабре&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. Перевод не мой, спасибо&lt;/em&gt; &lt;a href=&quot;http://habrahabr.ru/users/webMarshal/&quot;&gt;&lt;em&gt;webMarshal&lt;/em&gt;&lt;/a&gt;&lt;em&gt;-у за перевод.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you have any questions, or want to discuss some points — leave a comment here, or &lt;a href=&quot;https://twitter.com/skwee357&quot;&gt;tweet&lt;/a&gt; me.&lt;/p&gt;
&lt;p&gt;If we take a look at React component we can outline some properties.&lt;/p&gt;
&lt;h2 id=&quot;state&quot;&gt;State&lt;/h2&gt;
&lt;p&gt;Yes, each react component has state. State is something internal to the component. Only the component it self can read and write into its own state and as the name implies, the state of the component is used to store state (captain obvious here). Not interesting. Let’s move next.&lt;/p&gt;
&lt;h2 id=&quot;props&quot;&gt;Props&lt;/h2&gt;
&lt;p&gt;Or shall we say properties. Props is the data that affects the component display and behavior. Props can be optional or mandatory, and they are provided by the &lt;strong&gt;parent&lt;/strong&gt; component. Ideally, when you provide your component with the same props — it renders the same. Not interesting as well. Let’s move on.&lt;/p&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;/h2&gt;
&lt;p&gt;Meet context, the reason I’m writing this post. Context is an undocumented feature of react.js and it is similar in a way to props, but the only difference is that props are passed explicitly by parent to child, and they do not propagate down the hierarchy, while context can be requested by children component implicitly.&lt;/p&gt;
&lt;h3 id=&quot;but-why&quot;&gt;But why?&lt;/h3&gt;
&lt;p&gt;Good question. Lets draw!&lt;/p&gt;
&lt;figure class=&quot;rehype-figure&quot;&gt;&lt;img src=&quot;/images/react-state-props-tree.png&quot; alt=&quot;React state props tree#363x313&quot;&gt;&lt;figcaption&gt;React state props tree#363x313&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We have a Grandparent component that renders Parent A component that renders two child components: Child A and Child B.&lt;/p&gt;
&lt;p&gt;Lets say Grandparent component knows something that Child A and Child B wants to know as well, but Parent A doesn’t care about. Lets call this piece of data &lt;em&gt;Xdata.&lt;/em&gt; How would Grandparent component give ChildA and ChildB access to &lt;em&gt;Xdata&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Well, using Flux architecture we can store &lt;em&gt;Xdata&lt;/em&gt; inside a store and let Grandparent, Child A and Child B subscribe to that store. But what if we want Child A and Child B to be &lt;em&gt;pure dumb components&lt;/em&gt; that only renders some markup?&lt;/p&gt;
&lt;p&gt;Well, we than can pass &lt;em&gt;Xdata&lt;/em&gt; as prop to Child A and Child B. But Grandparent can not pass props to its grand children without going through Parent. And its not that big deal if we have only 3 level nesting, but real applications have a lot more nesting levels where top components acts as containers and leaf components are just markup. Well, we can use mixins that will automatically pass the props down the hierarchy for us, but its not elegant.&lt;/p&gt;
&lt;p&gt;Or we can use context. As I said earlier, context allows children component to request some data to arrive from component that is located higher in the hierarchy.&lt;/p&gt;
&lt;p&gt;Here is how it looks:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Grandparent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  childContextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getChildContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Jim&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Child&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Child&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  contextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;My name is &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Grandparent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is a &lt;a href=&quot;https://jsbin.com/yajobivuze/edit?js,output&quot;&gt;JSBin with the code&lt;/a&gt;. Change &lt;em&gt;Jim&lt;/em&gt; to &lt;em&gt;Jack&lt;/em&gt; and you see how your component re-renders.&lt;/p&gt;
&lt;h3 id=&quot;what-is-happening&quot;&gt;WHAT IS HAPPENING?!&lt;/h3&gt;
&lt;p&gt;Our Grandparent component says two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;I provide my children with context type named name of type string.&lt;/em&gt; This is what happening in &lt;em&gt;childContextTypes&lt;/em&gt; declaration.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The value of context type named name is Jim.&lt;/em&gt; This is what happening in &lt;em&gt;getChildContext&lt;/em&gt; function.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And our child component just says “Hey I expect to have context type named &lt;em&gt;name&lt;/em&gt;!” and it gets it. As far as I understand (and I’m far from being expert in react.js internals) when react renders children components it checks which components want to have context and those that want context are provided with context if parent supplies them.&lt;/p&gt;
&lt;h3 id=&quot;cool&quot;&gt;Cool!&lt;/h3&gt;
&lt;p&gt;Yep, expect when you encounter the following error&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Warning: Failed Context Types: Required context `name` was not specified in `Child`. Check the render method of `Parent`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;runner-3.34.3.min.js:1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Warning: owner-based and parent-based contexts differ (values: `undefined` vs `Jim`) for key (name) while mounting Child (see: http://fb.me/react-context-by-parent)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes of course I checked the link, its not very helpful.&lt;/p&gt;
&lt;p&gt;Here is the code that causes this (&lt;a href=&quot;https://jsbin.com/doqeyasako/edit?js,output&quot;&gt;JSBin&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Grandparent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Child&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Grandparent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Grandparent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  childContextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getChildContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Jim&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Child&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  contextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;My name is &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;It doesn’t make sense now, but Ill explain a setup where this is a viable hierarchy in the end of this post.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So what is happening?&lt;/p&gt;
&lt;p&gt;It took me a lot of time to understand why this happens. Googling the error only brought me to discussions by people who also wonders why this happens. I looked at other projects like react-router or react-redux that uses context to pass data down the component tree, when eventually I realized what is wrong.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I said I’m not expert in react internals, so everything I’m going to say now is purely what I understood about react internals. Leave a comment if you think I’m wrong (preferably with a link that backups your comment).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Remember I said that each react component have &lt;em&gt;state&lt;/em&gt;, &lt;em&gt;props&lt;/em&gt; and &lt;em&gt;context&lt;/em&gt;? Well, each component have as well so called &lt;em&gt;parent&lt;/em&gt; and &lt;em&gt;owner&lt;/em&gt;. And if we follow the link from the earlier warning (so yes, it is useful, I lied) we can understand that:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;In short, the owner of a component is whomever creates the component, while the parent of a component is whomever would be the containing ancestor in the DOM hierarchy.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It took me some time to understand this statement.&lt;/p&gt;
&lt;p&gt;So in my first example, the owner of Child component is Parent, as well as the parent of Child is Parent. While in the second example the owner of Child component is App while the parent is Parent.&lt;/p&gt;
&lt;p&gt;Context is something that propagates in a strange way to all the descendant components, but will be assigned only to those components who explicitly asked for context. But context does not propagate from the parent component as you’d expect but from owner! And since the owner of Child is App, React tries to find the “name” attribute in context of App instead of Parent or Grandparent.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/facebook/react/issues/2112&quot;&gt;Here is the relevant bug report in React&lt;/a&gt;. And here is the relevant &lt;a href=&quot;https://github.com/facebook/react/pull/3615&quot;&gt;pull request&lt;/a&gt; that should fix this to Parent based context as opposed to Owner based context in React 0.14.&lt;/p&gt;
&lt;p&gt;However React 0.14 is not there yet, so here is a fix (&lt;a href=&quot;https://jsbin.com/yujihowuwe/edit?js,output&quot;&gt;JSBin&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Grandparent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        {function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;          return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;              &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Child&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Grandparent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Grandparent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  childContextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  getChildContext&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;Jack&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    children&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Child&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;createClass&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  contextTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;PropTypes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;isRequired&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;My name is &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of instancing the Parent and Child components inside App, we return a function. Then inside the Grandparent we call this function hence making Grandparent the owner of Parent and Child components. Context propagates as expected now.&lt;/p&gt;
&lt;h2 id=&quot;ok-cool-but-why&quot;&gt;Ok, cool, but… WHY?!&lt;/h2&gt;
&lt;p&gt;Remember &lt;a class=&quot;internal new&quot; href=&quot;/articles/a-different-approach-for-localizing-react-js-app/&quot;&gt;my previous&lt;/a&gt; post about localization in react.js? Consider the following hierarchy:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Application&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; locale&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Dashboard&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SalesWidget&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocalizedMoney&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; currency&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;3133.7&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocalizedMoney&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;SalesWidget&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Dashboard&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Application&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;This is a static hierarchy, but usually you will have some routing and eventually you will create a situation where the owner and parent of your leaf components — differs.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Application is responsible for loading the locale data and initializing an instance of jquery/globalize. But it doesn’t use it. You don’t localize your top level components. Localization affects usually leaf components like localized text nodes, numbers, money display or time. And I’ve discussed earlier about the 3 possible ways to pass the globalize instance down the tree. Either we store it in a store and let leaf components subscribe to the store, but I think its incorrect. Leaf components should be pure dumb components.&lt;/p&gt;
&lt;p&gt;Passing the globalize instance as props can be tedious, imagine ALL your components requires globalize instance. Its the same as making globalize instance a global variable and let those who needs it — use it.&lt;/p&gt;
&lt;p&gt;But the most elegant way is using context. Application components says “Hey I have a globalize instance here, anyone who needs it let me know” and any Localized leaf components yells “Me! Me! I want it!”. This is an elegant solution. Leaf components stays pure, they dont depend on stores (yes they depend on context, but they have to, because they need it to render correctly). Globalize instance is not getting passed as props to your entire hierarchy. Every body is happy.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s it my friends!&lt;/p&gt;
&lt;p&gt;May the force be with you.&lt;/p&gt;</content:encoded></item><item><title>A different approach for localizing react.js app</title><link>https://kudmitry.com/articles/a-different-approach-for-localizing-react-js-app/</link><guid isPermaLink="true">https://kudmitry.com/articles/a-different-approach-for-localizing-react-js-app/</guid><pubDate>Tue, 18 Aug 2015 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Howdy!&lt;/p&gt;
&lt;p&gt;I’ve heard you use ReactJS? Good! I also heard you want to localize your application? That’s great! Well proper localization is hard, there are many js libraries for client side localization as well as &lt;a href=&quot;https://github.com/yahoo/react-intl&quot;&gt;yahoo/react-intl&lt;/a&gt; that provides localized react components based on JavaScript Intl API.&lt;/p&gt;
&lt;p&gt;I my self use es6 syntax and avoid using mixins, because &lt;a href=&quot;https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750&quot;&gt;mixins are dead&lt;/a&gt; (and well, because there is no easy way to integrate mixins into es6 syntax). So react-intl didn’t work for me. But damn I wanted those localized numbers! In this article I’m gonna show a different (not bad neither good, just different) approach on how I did it.&lt;/p&gt;
&lt;h2 id=&quot;localize-all-the-things&quot;&gt;Localize all the things!!&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/localize-meme.jpeg&quot; width=&quot;400&quot; height=&quot;400&quot; alt=&quot;Localize all the things!!&quot; class=&quot;object-fill m-auto&quot;&gt;&lt;/p&gt;
&lt;p&gt;I do like the idea of Intl API, why reinvent the wheel when we have &lt;a href=&quot;http://www.unicode.org/Public/cldr/26/&quot;&gt;51M of compressed localized data&lt;/a&gt; with support for dates, numbers, currencies and messages. So we do have &lt;a href=&quot;https://github.com/jquery/globalize&quot;&gt;jquery/globalize&lt;/a&gt; on client side to use all this data. The question is: How do I use it in a react.js application?&lt;/p&gt;
&lt;p&gt;Well, here is the general idea&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We need a way to identify user locale. Either we can guess it from the browser or (a preferred method, in my opinion) let the user choose his locale.&lt;/li&gt;
&lt;li&gt;Once we know the locale, we need to load the needed cldr json data (we can load everything, or just the parts we really need) into Globalize.&lt;/li&gt;
&lt;li&gt;….&lt;/li&gt;
&lt;li&gt;PROFIT!!!!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simple. Yet a bit complicated.&lt;/p&gt;
&lt;p&gt;One thing I disliked in react-intl is that each component knew about the locale data. If you have a nested tree of: App =&gt; Dashboard =&gt; SalesWidget =&gt; FormattedMoney, even though the locale data is needed only in FormattedMoney component, its passed to all the child components of App (react-intl handled that automatically using mixins). I offer a different solution. Locale data is no more than just… data! And data should be stored inside… yes right a Store!&lt;/p&gt;
&lt;h2 id=&quot;show-me-the-code&quot;&gt;Show me the code!&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I use&lt;/em&gt; &lt;a href=&quot;https://github.com/goatslacker/alt&quot;&gt;&lt;em&gt;alt&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as my flux implementation. Ideally my locale info should be fetched from the user, but since I do a MVP, I omitted this step.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Application component issues a UserFetch action that goes to the API and fetches the user details. Locale store listen to UserFetchCompleted action and then gets the locale from the user, and loads the needed cldr data.&lt;/p&gt;
&lt;p&gt;Here is how it looks&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;handleSetupStarted&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;waitFor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ProfileStore&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;dispatchToken&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; locale&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;en&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;//@TODO get from profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_URL&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;supplemental.json&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;json&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            request&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;CLDR_URL&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; locale&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &apos;.json&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&apos;json&apos;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;localeData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;                    self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;handleSetupFinished&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;localeData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fail&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handleSetupFailed&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fail&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;handleSetupFailed&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;handleSetupFinished&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;localeData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    Globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;supplemental&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;localeData&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;setState&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;translator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Globalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Yes, I know, I do an ajax call from the store. I couldn’t find a better way to do it. For some reason I wans’t able to dispatch an action inside the ProfileStore once the user data loaded because I got this weird message of: &lt;em&gt;Dispatch.dispatch(…): Cannot dispatch in the middle of a dispatch.&lt;/em&gt; and was lazy to investigate on how to fix it. Ideally this should be inside an action or an alt term called source. I’ll figure it out later).&lt;/p&gt;
&lt;p&gt;See, the handleSetupStarted waits for the ProfileStore to finish loading the user, and once its done, we get its locale and load 2 files: supplemental.json (which is a common set of data for every locale) and en.json (which is data related to the en locale). Once it finished loading, it creates a new instance of Globalize.&lt;/p&gt;
&lt;p&gt;Here is an example of a LocalizedMoney component&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Money&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B;font-style:italic&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocaleStore&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getState&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; amount&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; parseFloat&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      translator&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;translator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      cls&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; classNames&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        money&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        positive&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;amount&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &gt;=&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        negative&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;amount&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cls&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;translator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;formatCurrency&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;amount&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currency&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a usage example&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FormattedMoney&lt;/span&gt;&lt;span style=&quot;color:#D19A66;font-style:italic&quot;&gt; currency&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;1337.93&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;FormattedMoney&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See! No need to pass the locale data from the Application component to the LocalizedMoney component! LocalizedMoney just uses the Locale Store. You can even setup listener on localized components, to listen to changes on the Locale store, and when the user fires a Change Locale Action, you can change the locale on the fly without reloading the page. But I wouldn’t mind reloading the page. Locale is not something you change twenty times a day.&lt;/p&gt;
&lt;p&gt;You can as well implement caching for different formatters inside the LocaleStore as well, and instead of using the translator (which is just an instance of Globalize) directly, you can use something like&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;LocaleStore&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getCurrencyFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(“&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;USD&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;”).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;123.45&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which internally looks like (pseudo code):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getCurrencyFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyFormatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyFormatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;translator&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;currencyFormatter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyFormatters&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;currencyCode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: formatter identified not only by currency, but by options as well. You can have two USD formatters: One that shows the number as&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$1,000.00&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;And another one as&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1,000.00 US Dollars&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;In short: RTFM.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;a-note-about-cldr-shipping&quot;&gt;A note about CLDR shipping&lt;/h2&gt;
&lt;p&gt;The process of shipping the locale data should be a build step of your application. There is &lt;a href=&quot;https://www.npmjs.com/package/cldr-data-downloader&quot;&gt;cldr-data-downloader&lt;/a&gt;. Its a node module to download the CLDR data from unicode.org.&lt;/p&gt;
&lt;p&gt;I then use gulp to bundle the supplemental (common) and locale based data into merged json file.&lt;/p&gt;
&lt;p&gt;Less talk, more code:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:download&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  cldrDataDownloader&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;http://www.unicode.org/Public/cldr/&quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; CLDR_VERSION&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;/json.zip&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    cldrPath&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;      cb&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:supplemental&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; files&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;likelySubtags.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;currencyData.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;numberingSystems.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;plurals.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;    &quot;ordinals.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gulp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      files&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cldrPath&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;supplemental&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;supplemental.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;distDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr:bundle:locale&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; files&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;currencies.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;numbers.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  var&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; tasks&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; locales&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gulp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        files&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;          return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cldrPath&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;extend&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;locale&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;gulp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;distDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;cldr&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; gulpMerge&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;tasks&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point I need only currency formatting, so I use the required files in both supplemental bundle and locale bundle. &lt;a href=&quot;https://github.com/jquery/globalize#requirements&quot;&gt;jquery/globalize&lt;/a&gt; actually giving a nice table that shows, what json files from the cldr you need in order to use different localization modules.&lt;/p&gt;
&lt;h2 id=&quot;a-final-note&quot;&gt;A final note&lt;/h2&gt;
&lt;p&gt;For now I am happy with this approach. In my opinion it decouples the components from the actual localization implementation. Components stays reusable even if you decide to switch to a different localization implementation. Your store can expose an API like the &lt;em&gt;getCurrencyFormatter()&lt;/em&gt; I showed earlier, and actual implementation can be based on Intl, or your own. FormattedMoney component doesn’t care.&lt;/p&gt;
&lt;p&gt;Intl API is &lt;a href=&quot;http://caniuse.com/#search=Intl&quot;&gt;supported in all major browsers&lt;/a&gt; and those that do not support it, can be &lt;a href=&quot;https://github.com/andyearnshaw/Intl.js/&quot;&gt;polyfilled&lt;/a&gt;. Of course Intl API can be an overkill for most applications, especially if all you want is translating strings. But if your application requires displaying localized numbers, dates, times — I think there is no better solution other than Intl API.&lt;/p&gt;
&lt;p&gt;Best of luck and may the force be with you.&lt;/p&gt;</content:encoded></item></channel></rss>