<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jakub Senko</title>
    <description>The latest articles on Forem by Jakub Senko (@senky).</description>
    <link>https://forem.com/senky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1513897%2Ffe08cbde-e3bd-431d-8719-77d045ea11e1.png</url>
      <title>Forem: Jakub Senko</title>
      <link>https://forem.com/senky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/senky"/>
    <language>en</language>
    <item>
      <title>React timed server-side flip just right</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Thu, 21 Nov 2024 10:04:14 +0000</pubDate>
      <link>https://forem.com/senky/react-timed-server-side-flip-just-right-2311</link>
      <guid>https://forem.com/senky/react-timed-server-side-flip-just-right-2311</guid>
      <description>&lt;p&gt;One theme of web dev just keeps coming at me: server-side React. Last time, it was &lt;a href="https://www.artmann.co/articles/react-on-the-server-is-not-php" rel="noopener noreferrer"&gt;an excellent article from Christoffer Artmann&lt;/a&gt;. His argument about closing frontend/backend gap is so on point.&lt;/p&gt;

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

&lt;p&gt;And this single argument connects to Gergely Orosz's &lt;a href="https://www.youtube.com/watch?v=VpPPHDxR9aM" rel="noopener noreferrer"&gt;What is Old is New Again&lt;/a&gt; in my mind.&lt;/p&gt;

&lt;p&gt;You see, everybody is talking about layoffs, hiring freezes and difficulty for (even senior) software engineers to land a new job. I instead hear a strong signal from companies that &lt;strong&gt;it's time for function over art&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What do I mean by that? Until relatively recently it was so easy to convince exec team that we &lt;em&gt;need&lt;/em&gt; that microservice architecture. Or we &lt;em&gt;must&lt;/em&gt; try that new ORM lib before it is cool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpesx8mpn5id80zxjb0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpesx8mpn5id80zxjb0k.png" alt="A microservices meme showing small fishing boats each loaded with one container roped together." width="600" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ties have changed. Budgets are limited. We don't have time to play with new toys anymore. One guy who is "old-fashioned", yet able to deliver end-to-end is suddenly more valuable than all those specialists combined.&lt;/p&gt;

&lt;p&gt;In Gergely's words: Fullstack is in full swing.&lt;/p&gt;

&lt;p&gt;Not a good time for client-side UI libs. Unless... you go fullstack!&lt;/p&gt;

&lt;p&gt;There are two points that pop in my head:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Big pro: I am a big fan of single-language stack, so server-side TS gets my approval.&lt;/li&gt;
&lt;li&gt;Big con: React, even with server capabilities, is still just a UI library. We need Laravel-like framework to be able to beat that "old-fashioned" guy!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And so I am sure React team had great intuition with this decision. But we are still waiting for community to respond with a game-changing framework.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I will soon publish an article about my vision of Laravel-like React framework. If it got your attention, follow me!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>On responsibility and limited PTO</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Fri, 25 Oct 2024 12:34:37 +0000</pubDate>
      <link>https://forem.com/senky/on-responsibility-and-limited-pto-mc</link>
      <guid>https://forem.com/senky/on-responsibility-and-limited-pto-mc</guid>
      <description>&lt;p&gt;Every single software engineering job posting I read expects &lt;em&gt;responsibility&lt;/em&gt;. Almost none offer &lt;em&gt;unlimited PTO&lt;/em&gt; or at least non-8h workday. And I don't get it, because the two are closely related.&lt;/p&gt;

&lt;p&gt;We aren't a factory workers. Our work is &lt;strong&gt;creative&lt;/strong&gt; (as we create things). Creativity cannot be pushed, cannot be forced, and cannot be timed. It can be encouraged, either by &lt;a href="https://www.amazon.com/Real-Happy-Pill-Power-Moving/dp/151072298X" rel="noopener noreferrer"&gt;moving your body&lt;/a&gt;, focus times, exposing yourself to new experiences, or many other means. But it definitely doesn't obey to working hours.&lt;/p&gt;

&lt;p&gt;Sometimes I need to sleep on the problem. There is no point in waiting till 5pm, I won't move by an inch.&lt;/p&gt;

&lt;p&gt;Sometimes I need to solve my family problems before I can fully focus on the task at hand.&lt;/p&gt;

&lt;p&gt;Other times, after finishing stressful project, I need 2 weeks off. Even in December when PTO is used already.&lt;/p&gt;

&lt;p&gt;And sometimes I take 💩 to work for a nice 30min break nobody complains about.&lt;/p&gt;

&lt;p&gt;You get the point.&lt;/p&gt;




&lt;p&gt;Let's focus on the outcomes instead of outputs.&lt;/p&gt;

&lt;p&gt;Solve communication problems. Eradicate waterfall effects. Encourage collaboration. Promote knowledge sharing. Foster trust.&lt;/p&gt;

&lt;p&gt;And give freedom to the employees. 🙏&lt;/p&gt;

&lt;p&gt;Except for one instance. Unlimited PTO needs to be accompanied with a minimum!&lt;/p&gt;




&lt;p&gt;If you must, compare their performance, talk to managers from other companies to get the benchmark. But this is rarely needed when trust is living inside the company.&lt;/p&gt;

&lt;p&gt;You had enough courage to offer flexible working hours? Remote jobs? Great! Next stop - unlimited PTO. That's what &lt;em&gt;Freedom &amp;amp; responsibility&lt;/em&gt; ultimately means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I trust you can do this, do it your own way.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;And for the sake of your own success, apply the same to freelancers - do not charge them by the hour. 📅&lt;/p&gt;

</description>
      <category>responsibility</category>
      <category>freedom</category>
      <category>pto</category>
    </item>
    <item>
      <title>ONCE is apples, SaaS is oranges</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Thu, 12 Sep 2024 07:14:07 +0000</pubDate>
      <link>https://forem.com/senky/once-is-apples-saas-is-oranges-2pd2</link>
      <guid>https://forem.com/senky/once-is-apples-saas-is-oranges-2pd2</guid>
      <description>&lt;p&gt;&lt;em&gt;A reaction to David Heinemeier Hansson's (DHH) &lt;a href="https://www.youtube.com/watch?v=ZsHRjq8uk48" rel="noopener noreferrer"&gt;fireside chat&lt;/a&gt; and &lt;a href="https://youtu.be/UaWECi2Fmvk?si=Ms209K4Pc0s5nkNK&amp;amp;t=1681" rel="noopener noreferrer"&gt;ScaleUp&amp;amp;Up podcast&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;David has talked about a lot of topics, and two most resonating ones are &lt;em&gt;cloud exit&lt;/em&gt; and &lt;em&gt;the once model&lt;/em&gt;. Theo has &lt;a href="https://www.youtube.com/watch?v=FnmZhXWohP0" rel="noopener noreferrer"&gt;published a great video reaction&lt;/a&gt; on &lt;em&gt;cloud exit&lt;/em&gt;, so let me focus on &lt;em&gt;the once model&lt;/em&gt; only - and why I think he omits a lot of important aspects to portray &lt;em&gt;the once model&lt;/em&gt; as a good business strategy.&lt;/p&gt;

&lt;p&gt;First, let me acknowledge that I think &lt;em&gt;the once model&lt;/em&gt; wraps not one paradigm change, but two actually, and this realization is important. First, it removes "aaS" from SaaS, leaving you with bare software, no service. Second, it replaces a monthly pricing with one-time payment. I will touch on both changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really once
&lt;/h2&gt;

&lt;p&gt;In the podcast, DHH talks about Adobe as a good example of &lt;em&gt;the once model&lt;/em&gt;-driven successful company. However, in reality Adobe was a &lt;em&gt;once-a-year&lt;/em&gt; fee company. I don't know any graphics designer who didn't upgrade to the newest version every time it got released, and Adobe knew that very well. It forced Adobe to innovate faster, probably, because each major release had to be that much better to persuade customers to purchase again, but I don't think it is honest to call that kind of pricing form &lt;em&gt;the once model&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Funny enough, it's the same with &lt;a href="https://once.com/campfire" rel="noopener noreferrer"&gt;Campfire&lt;/a&gt; - updates are provided &lt;a href="https://once.com/campfire#updates" rel="noopener noreferrer"&gt;for free&lt;/a&gt; only up to next major version.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really cheap
&lt;/h2&gt;

&lt;p&gt;Companies don't usually think in terms of total valuation, but rather in cash flow. It is difficult for company to purchase an expensive item, that's why loans, leasing and other financial tools exist. I dare to say that SaaS monthly pricing is another such tool, because it allows your company to skip the bank and purchase highly complex and expensive software as a loan really. With almost all risks moved to SaaS vendor. Monthly pricing is a financial leverage!&lt;/p&gt;

&lt;p&gt;Back to Campfire. It is unbelievably cheap. I wonder if it is profitable even. But even if it is, the price doesn't reflect all the inputs other companies had to deliver or are constantly delivering, see next points.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really all-in-one
&lt;/h2&gt;

&lt;p&gt;Support. Let's look at Campfire again. It only &lt;a href="https://once.com/campfire#support" rel="noopener noreferrer"&gt;includes bare bones support&lt;/a&gt;. While any SaaS keeps customer support up and running to satisfy customers every single month, &lt;em&gt;the once model&lt;/em&gt; just skips that part altogether. You may argue that some customers want this support-excluded deal and you are probably right. I only argue that those products aren't batteries included and that is reflected in the price.&lt;/p&gt;

&lt;p&gt;The same applies to running costs - &lt;em&gt;the once model&lt;/em&gt; just offloads this to the customer. This may be just "running one command", but I rather think it means hiring a DevOps - not cheap, and not once. Not to mention that SaaS has ability to optimize running costs by centralizing it while self-hosted server most probably stays underutilized for the most of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really innovative
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you’ve used Slack or Teams, you already know how to chat with Campfire.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the problem! All the research towards UX was carried over by Slack, Teams, WhatsApp and who knows who else. I am not against copying good designs and user patterns. But I have a feeling that this particular product is cannibalizing on that, allowing it to lower its price. Again, it is actually good that we are able to optimize production, but in the world where everything is &lt;em&gt;the once model&lt;/em&gt; we may have no innovation eventually!&lt;/p&gt;

&lt;p&gt;It's not about production costs only. Take Spotify vs iTunes example. Before Spotify was a thing we purchased every song for 1 USD. Can you imagine that now? Spotify has revolutionalized the way we purchase music, solving music pirating in totally unexpected way. Not to mention that barriers to discover new authors are ridiculously low, almost non-existent today. I don't want to return to &lt;em&gt;the once model&lt;/em&gt; in music.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really protecting the business
&lt;/h2&gt;

&lt;p&gt;I am old enough to remember cracking software that bypassed &lt;em&gt;the once model&lt;/em&gt; software's license verification to use it for free. Adobe remembers it well, too. Can you fight this? I guess you can, partnering with OS producers, a big lawyer firm, or by other means - all costly, reflecting in the final price. SaaS, by running software under its own conditions, is avoiding this trap, allowing further price optimizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really simple
&lt;/h2&gt;

&lt;p&gt;Imagine you'd have to manage servers for every single software your company owns. If you don't employ a senior DevOps your hard-earned company might suddenly be offline, hacked or compromised in a thousand other ways. There is nothing simple in running servers, starting from physical aspects like material failure, up to security holes in (not updated) OS, or even &lt;em&gt;the once model&lt;/em&gt; product itself. David himself is hard proponent of compressing abstractions, and in my point of view, cloud compresses servers, and SaaS compresses running software on those servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not really the same thing
&lt;/h2&gt;

&lt;p&gt;Ultimately, you may think that using &lt;em&gt;the once model&lt;/em&gt; is just a relic of the past. I don't know. Maybe it is a pendulum swinging back, but maybe it is a model long-overcome. Only time will tell, and I want to thank David for doing it, because it is to the benefit of us all when businesses challenge standards.&lt;/p&gt;

&lt;p&gt;So &lt;em&gt;the once model&lt;/em&gt; vs SaaS? Let's just remember that we cannot compare apples to oranges.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Trivia: I always enjoy listening to someone who is highly philosophical, an art I don't see often in these times.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fingers crossed, David! 🤞&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Remember "divide and rule"</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Mon, 09 Sep 2024 07:14:01 +0000</pubDate>
      <link>https://forem.com/senky/remember-divide-and-rule-bim</link>
      <guid>https://forem.com/senky/remember-divide-and-rule-bim</guid>
      <description>&lt;p&gt;&lt;em&gt;Divide and rule&lt;/em&gt;, a strategy known for millennia, from Roman Empire, to Napoleon, to British Empire is not just another military or political technique. It is one of the best tools I have ever used to conquer my enemies - technical and business problems.&lt;/p&gt;

&lt;p&gt;You may not realize this, but &lt;em&gt;divide and rule&lt;/em&gt; is everywhere around you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;feature flags &amp;amp; A/B tests,&lt;/li&gt;
&lt;li&gt;extracting part of code to separate function,&lt;/li&gt;
&lt;li&gt;separating concerns (e.g. MVC pattern or per-domain folders),&lt;/li&gt;
&lt;li&gt;ICE scoring and implementing just top of the list,&lt;/li&gt;
&lt;li&gt;ideation / research / design / implementation sequence,&lt;/li&gt;
&lt;li&gt;and many, many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the good examples. These are the times when you are the emperor utilizing &lt;em&gt;divide and rule&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;However, just like fire is good servant but a bad master, we all sometimes become the object of this grand strategy when we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;allow being distracted from deep focus,&lt;/li&gt;
&lt;li&gt;refuse to communicate, throttling the spread of knowledge,&lt;/li&gt;
&lt;li&gt;accept "while you are looking at that part of the code, why don't you fix this long-standing bug there as well" aka context switching,&lt;/li&gt;
&lt;li&gt;implement feature before giving it a thought,&lt;/li&gt;
&lt;li&gt;trying the same procedure over and over when debugging,&lt;/li&gt;
&lt;li&gt;looking for faulty commit one by one (instead of &lt;code&gt;git bisect&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;don't take apart our challenges into screws (when estimating time or trying to understand the complex concept),&lt;/li&gt;
&lt;li&gt;and the list goes on.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;So think about the forces that try to divide &lt;em&gt;you&lt;/em&gt;, and come with the ways to divide &lt;em&gt;them&lt;/em&gt; instead.&lt;/p&gt;

&lt;p&gt;And next time you are stuck on a problem, ask this simple question: how can I &lt;em&gt;divide&lt;/em&gt; this problem into smaller parts? It will result in &lt;em&gt;impera&lt;/em&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>"AI" will not replace you</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Tue, 20 Aug 2024 14:37:56 +0000</pubDate>
      <link>https://forem.com/senky/ai-will-not-replace-you-lc9</link>
      <guid>https://forem.com/senky/ai-will-not-replace-you-lc9</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is the last part of a series of notes on &lt;a href="https://www.pragmaticengineer.com/" rel="noopener noreferrer"&gt;Gergely Orosz&lt;/a&gt;'s &lt;a href="https://www.youtube.com/watch?v=VpPPHDxR9aM" rel="noopener noreferrer"&gt;What is Old is New Again&lt;/a&gt; talk that attempts to put his predictions (that strongly resonate with me) into practical steps for smart software engineers.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  "&lt;a href="https://youtu.be/VpPPHDxR9aM?si=z1iYq_P8xZTUy2HK&amp;amp;t=2545" rel="noopener noreferrer"&gt;Finally, the elephant in the room: AI&lt;/a&gt;"
&lt;/h2&gt;

&lt;p&gt;Few weeks ago my brother, who is software developer, told me that he is afraid that AI will replace us. While I understand his worries, I was never afraid of such scenario.&lt;/p&gt;

&lt;p&gt;Yes, AI (let's call it by its true name: LLM - large language model) looks freaking smart when used for the first time. It "understands" you in almost any human language, can reply in that same language and can produce very appealing answer - be it text, image, structured data, code or even a video. It can seemingly consume all human knowledge into one gigantic mind and throw PhD-level answers in all industries at the same time.&lt;/p&gt;

&lt;p&gt;Until you start using it daily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limits we don't want to see
&lt;/h3&gt;

&lt;p&gt;A word &lt;em&gt;hallucinations&lt;/em&gt; become almost as popular as AI, because LLM often outputs what looks like a competent answer, yet it is completely off.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvqdegvllqhzuz0cdsh8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvqdegvllqhzuz0cdsh8.jpg" alt="Source: https://www.sify.com/ai-analytics/the-hilarious-and-horrifying-hallucinations-of-ai/"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of that, if you ever tried to use chat LLM for a longer discussion, you most definitely realized at some point that it stops considering your commands given earlier. This phenomenon is called "context overflow", and simply means that the LLM isn't able to hold that much context, although for a human mind it might seem minimal.&lt;/p&gt;

&lt;p&gt;Context overflow, however, is just the symptom of much deeper problem, which is the cost of running LLMs. Every chat, image prompt or code suggestion is very, very expensive. We will eventually optimize this (e.g. Github Copilot is quite cheap for the value it brings), but keep in mind that most of LLMs today are burning money and given higher interest rate this will not last for long. 💰&lt;/p&gt;

&lt;p&gt;The biggest problem of all is, however, the fact that LLMs train more and more on its own output, eventually collapsing into world of hallucinations. And while some LLM producers claim to be able to detect AI text, this is ultimately an endless race.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not a first rodeo
&lt;/h3&gt;

&lt;p&gt;Also, let us remind that this is not the first AI hype. You can read more about AI history &lt;a href="https://www.njii.com/2024/05/ai-hype-cycles-lessons-from-the-past-to-sustain-progress/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/AI_winter" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://medium.com/@buschmuc/the-big-ai-hype-lessons-to-be-learnt-from-the-past-abc24e79f8ca" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://www.tsvcap.com/post/what-hype-trends-in-history-can-say-about-the-ai-hype-today" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Tldr; it is getting better every time, but I don't think this time is the ultimate one - especially because it looks like we already &lt;a href="https://www.youtube.com/watch?v=Y8Ym7hMR100" rel="noopener noreferrer"&gt;hit the plateau&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The decision maker
&lt;/h3&gt;

&lt;p&gt;Now to my final argument. "AI" isn't gonna replace you, because it, in current form and shape, lacks two deal-breaker attributes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It isn't creative&lt;/strong&gt;, meaning it only outputs what has already been out there in the past.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And &lt;strong&gt;it cannot make decisions&lt;/strong&gt;. I literally tried to force ChatGPT into making business decision by "acting as CEO", but it refused. It gave me some answers, but also emphasized that I should take them as suggestions, not decisions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And this is the reason why it won't replace your creative job of architecting and building software. It does make us super-productive (thanks for that!), but it isn't nearly close to becoming a decision-maker you, as a software engineer, need to be basically constantly.&lt;/p&gt;

&lt;p&gt;So don't worry about your job. Instead, embrace the LLMs and become more productive thanks to them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: "AI" is already everywhere
&lt;/h2&gt;

&lt;p&gt;From fraud detection, recommendations, predictions, digital assistants (Siri, Alexa), translations, healthcare, advertising, weather, etc. - usually just called neural networks, world is full of AI that is not LLM, but a serious business value tool nonetheless. Let's not forget to look at the bigger picture in times of unease.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>decision</category>
      <category>creativity</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Product engineers</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Thu, 15 Aug 2024 09:10:01 +0000</pubDate>
      <link>https://forem.com/senky/product-engineers-p8j</link>
      <guid>https://forem.com/senky/product-engineers-p8j</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of a series of notes on &lt;a href="https://www.pragmaticengineer.com/" rel="noopener noreferrer"&gt;Gergely Orosz&lt;/a&gt;'s &lt;a href="https://www.youtube.com/watch?v=VpPPHDxR9aM" rel="noopener noreferrer"&gt;What is Old is New Again&lt;/a&gt; talk that attempts to put his predictions (that strongly resonate with me) into practical steps for smart software engineers.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  "&lt;a href="https://youtu.be/VpPPHDxR9aM?si=MxnL1AGLSFFpriG-&amp;amp;t=2429" rel="noopener noreferrer"&gt;Become more product-minded / business-minded&lt;/a&gt;"
&lt;/h2&gt;

&lt;p&gt;I like to call product-minded engineers simply a product engineers, because it is important for me to acknowledge, that I own the product I am building.&lt;/p&gt;

&lt;p&gt;It is not a nuance.&lt;/p&gt;

&lt;p&gt;I have seen engineers not giving much thought about what their code is going to be used for. I have seen engineers not testing their code, because (their own words) the company pays for a dedicated QA person. I have seen engineers not releasing their code to production, leaving it for others (including emergency rollbacks). I have seen engineers ignoring analytics.&lt;/p&gt;

&lt;p&gt;And those engineers, IMHO, do not understand the single most important aspect of building a successful, effective software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ownership
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I did what the task said, why do you put blame on me?&lt;br&gt;
-&lt;em&gt;a random engineer&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy3tsfuhclct3q8smact.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy3tsfuhclct3q8smact.png" alt="Great chasm between product and technology teams." width="674" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In some companies, I assume, there is a great chasm in between technology and product people. Product people think about &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt;, and then produce a task. Technology then solves the &lt;em&gt;how&lt;/em&gt; and deploys the output. If the output backfires, technology puts the blame on product guys, because engineers just did what PMs said.&lt;/p&gt;

&lt;p&gt;As soon as you, as an engineer, degrade your presence in the company into &lt;em&gt;how&lt;/em&gt;, you essentially became a workforce producing code. And you know machines do replace workforce, do you?&lt;/p&gt;

&lt;p&gt;We will never be able to take &lt;em&gt;what&lt;/em&gt; from product, because that is the essence of their roles (that's a good thing!), but we can - and should - contribute to &lt;em&gt;why&lt;/em&gt;, if not to both.&lt;/p&gt;

&lt;p&gt;And this is done by firstly acknowledging, that we, as product and technology &lt;strong&gt;together&lt;/strong&gt;, own the product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38wegd1bjuv5i6a7xvl5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38wegd1bjuv5i6a7xvl5.png" alt="An intersection between product and technology teams." width="546" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Influence
&lt;/h3&gt;

&lt;p&gt;As soon as you take ownership seriously, a door to product world opens up and you can observe, understand, and influence the business decisions. This is extremely important, because your proactivity can produce or save huge money. And whatever your boss told you about vision, mission and values - just remember that (except for non-profits) your company exists to make profit. The more you contribute, the more valuable you are. 💸&lt;/p&gt;

&lt;p&gt;Gergely &lt;a href="https://blog.pragmaticengineer.com/the-product-minded-engineer/" rel="noopener noreferrer"&gt;wrote an amazing blog&lt;/a&gt; with practical steps towards this attitude.&lt;/p&gt;

&lt;p&gt;And this mindset is simply invaluable in recessions, where every dollar earned or saved is celebrated. You essentially become recession-resistant engineer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Do not be afraid to speak up
&lt;/h2&gt;

&lt;p&gt;It may be tempting to be silent during brainstorming / ideation sessions as an engineer (or not attend at all), because you may thing that product people have much more information about the subject. I urge you to raise your voice for two main reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your perspective is different.&lt;/strong&gt; They hear customers (current ones and the future ones), query analytics, see business implications and are trying to make meaningful compromise. But only you can see that the analytics isn't set up correctly and the data cannot be trusted. Only you can see that one feature will take 10x less time to build. Only you can see that the external tool they want to use is missing an API - an important piece for automation. Do raise your voice, do not be afraid to look stupid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your opinion is equal to the others.&lt;/strong&gt; This is especially true for brainstormings. I have experienced this an endless times. What sounds like a stupid idea in my head wins the most votes. You just never know.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Stay tuned for the third note in which I will conclude this series with my thoughts on "AI" replacing engineers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ownership</category>
      <category>influence</category>
      <category>product</category>
      <category>recession</category>
    </item>
    <item>
      <title>Javascript is without a fullstack framework (and will miss fullstack era this time)</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Mon, 05 Aug 2024 09:23:52 +0000</pubDate>
      <link>https://forem.com/senky/javascript-is-without-a-fullstack-framework-and-will-miss-fullstack-era-this-time-22bn</link>
      <guid>https://forem.com/senky/javascript-is-without-a-fullstack-framework-and-will-miss-fullstack-era-this-time-22bn</guid>
      <description>&lt;p&gt;&lt;em&gt;This article starts a series of notes on &lt;a href="https://www.pragmaticengineer.com/" rel="noopener noreferrer"&gt;Gergely Orosz&lt;/a&gt;'s &lt;a href="https://www.youtube.com/watch?v=VpPPHDxR9aM" rel="noopener noreferrer"&gt;What is Old is New Again&lt;/a&gt; talk that attempts to put his predictions (that strongly resonate with me) into practical steps for smart software engineers.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  "&lt;a href="https://youtu.be/VpPPHDxR9aM?si=s2fqHNEVFJd2-A9o&amp;amp;t=1800" rel="noopener noreferrer"&gt;Fullstack is in full swing&lt;/a&gt;"
&lt;/h2&gt;

&lt;p&gt;When you start thinking about fullstack frameworks, you most probably think about &lt;em&gt;the big three&lt;/em&gt; brothers: PHP's &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt;, Ruby on &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt; and Python's &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now narrow your scope down to JS/TS.&lt;/p&gt;

&lt;p&gt;Anything?&lt;/p&gt;

&lt;p&gt;If you ever heard of &lt;a href="https://redwoodjs.com/" rel="noopener noreferrer"&gt;RedwoodJS&lt;/a&gt;, &lt;a href="https://adonisjs.com/" rel="noopener noreferrer"&gt;AdonisJS&lt;/a&gt; or &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt;, those might be the closest ones. But let's be honest, nobody is talking about them, few are using them, and they definitely didn't grow enough to be put on pair with the &lt;em&gt;the big three&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And they still lack features we love and use every day (such as CLI, queues, (DB) models, and more) and/or ease of use/development/deployment, tests, community, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js?
&lt;/h3&gt;

&lt;p&gt;Let's talk about &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, probably the most popular JS framework these days. Vercel (owner of Next.js) doesn't mention "fullstack" keyword anywhere on the visible side of the website, yet their meta descriptions says "the full-stack React framework for the web". I'd challenge their choice of words, but I am sure SEO comes to play here as well. In my definition, fullstack comes with a complete set of tools to cover development from end to end, yet Next.js doesn't care about DB communication/schema/migrations, authentication, authorization, and so much more. Recent &lt;a href="https://remix.run/blog/merging-remix-and-react-router" rel="noopener noreferrer"&gt;merge of Remix (main competitor) with React Router&lt;/a&gt; just shows the harsh truth - that these frameworks are just a router on steroids.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Javascript way
&lt;/h3&gt;

&lt;p&gt;Then there is &lt;a href="https://create.t3.gg/" rel="noopener noreferrer"&gt;The T3 Stack&lt;/a&gt;, a mere collection of libraries that got more stars than two our of three JS fullstack libraries mentioned above!&lt;/p&gt;

&lt;p&gt;And with recent trends being just about "&lt;a href="https://2023.stateofjs.com/en-US/conclusion/" rel="noopener noreferrer"&gt;moving to the server&lt;/a&gt;" (State of JS 2023 conclusion from Theo Browne, T3 Stack creator), we are clearly far from fullstack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action points
&lt;/h2&gt;

&lt;p&gt;So my predictions are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fullstack era will indeed come.&lt;/li&gt;
&lt;li&gt;NestJS won't become popular enough, and thus JS will miss this round of fullstack frameworks prevalence.&lt;/li&gt;
&lt;li&gt;Companies will use &lt;em&gt;the big three&lt;/em&gt; or a combination of TS libraries (T3 Stack or similar) to stay one-lang.&lt;/li&gt;
&lt;li&gt;Knowing TypeScript is a must.&lt;/li&gt;
&lt;li&gt;In order to remain relevant in the job market, we need to be fluent with at least one of &lt;em&gt;the big three&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My personal action point is to learn Django. I have past (working) experience with Laravel, some (university) experience with Rails, but I never touched Django, and barely worked with Python. Time to change that!&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: An interesting approach
&lt;/h2&gt;

&lt;p&gt;What I appreciate seeing in recent years is the attempt to form fullstack JS/TS framework from different angle, and that is from frontend to backend (opposite of where all &lt;em&gt;the big three&lt;/em&gt; came from). I can see the trend all over the place, with &lt;a href="https://nextjs.org/learn/dashboard-app/partial-prerendering" rel="noopener noreferrer"&gt;Partial Prerendering&lt;/a&gt; in Next.js, to &lt;a href="https://docs.astro.build/en/concepts/islands/" rel="noopener noreferrer"&gt;Astro Islands&lt;/a&gt;, to &lt;a href="https://kit.svelte.dev/docs/load#streaming-with-promises" rel="noopener noreferrer"&gt;Streaming&lt;/a&gt; in SvelteKit. All &lt;em&gt;the big three&lt;/em&gt; struggle with frontend interactivity, because they need JS to serve that purpose. What if we solved interactivity first, and then added all the boring (but mandatory) tools to JS frameworks later? Only time will tell if this seemingly weird approach bears fruit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Stay tuned for the second note in which I will discuss how product/business-minded will get (un)fair advantage in their next job hunt.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>fullstack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Case for explicit error handling</title>
      <dc:creator>Jakub Senko</dc:creator>
      <pubDate>Fri, 24 May 2024 08:06:33 +0000</pubDate>
      <link>https://forem.com/senky/case-for-explicit-error-handling-1hcd</link>
      <guid>https://forem.com/senky/case-for-explicit-error-handling-1hcd</guid>
      <description>&lt;p&gt;One of the best articles on front-end app architecture I have ever read suggested not-so-popular patterns, including headless approach (data and logic are strictly separated from UI - e.g. React), explicit initialization (manual dependency injection), or reactivity aversion (no &lt;code&gt;useEffect&lt;/code&gt;, or MobX's &lt;code&gt;reaction&lt;/code&gt;). Over the years I have seen the benefits of all the patterns except for one.&lt;/p&gt;

&lt;p&gt;It is called &lt;em&gt;Concentrated error handling&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The pattern suggests designing code so that error handling doesn't have to be written for each individual call that can fail. It even encourages to treat &lt;code&gt;catch&lt;/code&gt; clauses as code smell.&lt;/p&gt;

&lt;p&gt;My experience just can't seem to find peace with this pattern. Let me give you my biggest concern wight away:&lt;/p&gt;

&lt;p&gt;User experience.&lt;/p&gt;

&lt;center&gt;🥰&lt;/center&gt;

&lt;h2&gt;
  
  
  Why user experience matters the most
&lt;/h2&gt;

&lt;p&gt;You see, we are talking about &lt;em&gt;front-end&lt;/em&gt; apps here. They are specifically meant to provide UI to user. They exist &lt;em&gt;for&lt;/em&gt; the user. And if you handle your errors in one global error boundary, you compromise user experience. How?&lt;/p&gt;

&lt;p&gt;I have been working on a project recently that relied on a single error boundary to do all kinds of stuff we do when our app throws an error:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;log to console&lt;/li&gt;
&lt;li&gt;track to monitoring software&lt;/li&gt;
&lt;li&gt;show error message to user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Can you guess how the error message toast looked like? Yep:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas8869mgdqfvby0o87vl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas8869mgdqfvby0o87vl.png" alt="Unhelpful error message" width="328" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is wrong on so many levels! User must be able to &lt;a href="https://www.nngroup.com/articles/error-message-guidelines/" rel="noopener noreferrer"&gt;read the error message in plain language, understand the problem, and ideally be able to recover from it&lt;/a&gt; (Jakob Nielsen's 9th heuristic rule). But with one (or even multiple) error boundaries you (most of the time) aren't in the right context anymore to be able to provide user with the &lt;em&gt;least painful experience&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let me show you how I envision error handling in front-end apps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am using &lt;code&gt;fetch&lt;/code&gt; as an example, but this can be anything from websocket connection, browser APIs, library &amp;amp; framework calls, etc.&lt;/em&gt;&lt;/p&gt;

&lt;center&gt;✨&lt;/center&gt;

&lt;h3&gt;
  
  
  Separate severe errors from enhancements
&lt;/h3&gt;

&lt;p&gt;Sometimes data we show aren't strictly needed for the user to perform the task. For example at slido.com's &lt;a href="https://www.slido.com/powerpoint-polling" rel="noopener noreferrer"&gt;PowerPoint page&lt;/a&gt;, information about version and package size are nice-to-have, but user is still able to download the integration even if endpoint with those information failed. We do not need to show error message to user. Instead, we do nothing - and that's user's &lt;em&gt;least painful experience&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You will be surprised how much information is optional once you start paying attention to it. Are you deleting a team in any SaaS app? A list of users of that team is usually a good visual indicator for the admin of how severe the action is. But if that request fails, admin is still able to delete the team - and therefore bombarding him/her with messages about failed request is absolutely not &lt;em&gt;least painful experience&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With global error boundary, you don't get enough information easily to decide whether the error comes from enhancement or not. Take this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/version-and-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// All info you have here is generic `TypeError ` in `error` variable.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We literally have no idea what endpoint failed. Therefore we cannot decide if response data are enhancement or mandatory information.&lt;/p&gt;

&lt;p&gt;Whereas in this slight change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/version-and-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Code context gives you hint that this failure is just enhancement.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code itself gives you enough context to react to error appropriately - that is to ignore it.&lt;/p&gt;

&lt;center&gt;🛠️&lt;/center&gt;

&lt;h3&gt;
  
  
  React to error in the best manner possible
&lt;/h3&gt;

&lt;p&gt;Now that you purposefully ignored enhancements, let's discuss handling of severe errors.&lt;/p&gt;

&lt;p&gt;Imagine social network such as dev.to where blog posts functionality is present. You want to edit a post. Two possible points of failure are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;loading current post content into the editor,&lt;/li&gt;
&lt;li&gt;saving edited content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both cases can fail on the same network level giving you exactly the same error, yet handling should be radically different:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When post content fails to load, a big error message (maybe replacing whole editor) should ask user to attempt to reload the page or manually re-trigger content fetching.&lt;/li&gt;
&lt;li&gt;If saving fails, you most definitely don't want to remove the edited content with big error message. Instead, a toast informing about unsuccessful saving asking for a retry later is correct way to do it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't possible with global error boundary, because, again, you don't have the context (and maybe also access to models/components that need to be updated).&lt;/p&gt;

&lt;p&gt;This article doesn't discuss the best patters for error handling, but there is &lt;a href="https://www.nngroup.com/articles/error-message-guidelines/" rel="noopener noreferrer"&gt;tons&lt;/a&gt; &lt;a href="https://www.smashingmagazine.com/2022/08/error-messages-ux-design/" rel="noopener noreferrer"&gt;of&lt;/a&gt; &lt;a href="https://uxplanet.org/error-message-guidelines-6ce257d3d0bd" rel="noopener noreferrer"&gt;materials&lt;/a&gt; &lt;a href="https://design4users.com/ux-design-error-screens-and-messages/" rel="noopener noreferrer"&gt;about&lt;/a&gt; &lt;a href="https://talebook.io/blog/error-states/" rel="noopener noreferrer"&gt;this&lt;/a&gt; &lt;a href="https://www.pencilandpaper.io/articles/ux-pattern-analysis-error-feedback" rel="noopener noreferrer"&gt;topic&lt;/a&gt; on the Internet.&lt;/p&gt;

&lt;center&gt;🫂&lt;/center&gt;

&lt;h3&gt;
  
  
  Code colocation is your friend
&lt;/h3&gt;

&lt;p&gt;You are probably thinking: hey Jakub, I can write a generic wrapper around fetch that will catch &lt;code&gt;TypeError&lt;/code&gt;s, add necessary data to it and re-throw it. That way my global error boundary has the context you are talking about.&lt;/p&gt;

&lt;p&gt;And you are right, except for one thing. You start rolling your spaghetti if you ever use that additional context for conditional actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EnrichedError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/version-and-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// just enhancement&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;posts&lt;/span&gt;&lt;span class="se"&gt;\/\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;edit/&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;mainController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postEditModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/edit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;mainController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saving failed. Please try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what I call a code smell! Instead, let's leverage existing (implicit) context and also make sure that related code sits together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/version-and-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// just enhancement&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/edit`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// notice we don't access `mainController` anymore because we are in the context of `postEditModel`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/edit`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;mainController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UIController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saving failed. Please try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;center&gt;🌐&lt;/center&gt;

&lt;h3&gt;
  
  
  On global error boundaries
&lt;/h3&gt;

&lt;p&gt;Are global error boundaries any good then? I think so! They excel at tasks that you want to perform for every error out there: logging to console, tracking to your favorite monitoring tool, or even as a fallback for all the errors that aren't explicitly handled (although I'd say log those cases just so that you can add explicit handling later).&lt;/p&gt;

&lt;p&gt;Also, top-level error handler is important for the user, because it serves as a last resort for catching unhandled errors and displaying meaningful error page to the user.&lt;/p&gt;

&lt;p&gt;With this in mind you can &lt;em&gt;sometimes&lt;/em&gt; purposefully handle errors implicitly. For example if app initialization relies on a request and app cannot be initialized without it, you can ignore try/catch block there and let global error handler display unrecoverable error page to the user.&lt;/p&gt;

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

&lt;p&gt;Global error handling compromises user experience. Handle errors explicitly to leverage its implicit context. It will help you choose the best reaction to error to optimize for &lt;em&gt;least painful experience&lt;/em&gt; for your user. Use global error handler to catch and track yet unhandled errors. Remember: you make front-end apps, user comes first.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>architecture</category>
      <category>errors</category>
      <category>ux</category>
    </item>
  </channel>
</rss>
