<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Yumashev</title>
    <description>The latest articles on DEV Community by Alex Yumashev (@alexjitbit).</description>
    <link>https://dev.to/alexjitbit</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%2F141036%2F97602745-21c9-4024-b70a-5e07975a725b.jpeg</url>
      <title>DEV Community: Alex Yumashev</title>
      <link>https://dev.to/alexjitbit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexjitbit"/>
    <language>en</language>
    <item>
      <title>No, Wall Street, DeepSeek is not "far superior"</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Tue, 04 Feb 2025 20:54:22 +0000</pubDate>
      <link>https://dev.to/alexjitbit/no-wall-street-deepseek-is-not-far-superior-51c7</link>
      <guid>https://dev.to/alexjitbit/no-wall-street-deepseek-is-not-far-superior-51c7</guid>
      <description>&lt;p&gt;&lt;strong&gt;I mean, it is!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But the whole story about the stock market reacting to the news about DeepSeek V3 and R1 is a fine example of the knee-jerk nature of mass consciousness in the era of clickbait economics.&lt;/p&gt;




&lt;p&gt;Briefly, by points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No, DeepSeek isn’t “head and shoulders above” every other model.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The results vary across benchmarks, but on average, GPT-4o and Gemini-2 are better. You can see this on ChatBot Arena, for example (&lt;a href="https://www.reddit.com/r/LocalLLaMA/comments/1i8u9jk/deepseekr1_appears_on_lmsys_arena_leaderboard/" rel="noopener noreferrer"&gt;Reddit thread&lt;/a&gt;). Even in the results published by DeepSeek’s authors themselves (&lt;a href="https://github.com/deepseek-ai/DeepSeek-V3/blob/main/figures/benchmark.png" rel="noopener noreferrer"&gt;benchmark graph&lt;/a&gt;), you can see that in several tests, the model lags behind GPT-4o from May 2024—which, mind you, is currently ranked 16th on ChatBot Arena.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No, training DeepSeek didn’t cost $6 million, “100 times less than GPT-4.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The $6 million figure refers only to the final training run of the published model. It doesn’t include any prior experiments, earlier versions, or R&amp;amp;D costs. This is just the raw computational cost of that final training run. And guess what? That figure is pretty much in line with models of the same class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No, Nvidia did not deserve this hit&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not that we’re shedding tears for them — they could use a push to lower hardware prices. And let's not forget that DeepSeek was still trained on Nvidia’s own hardware. And no, their GPUs aren’t suddenly obsolete. DeepSeek’s computational budget is fairly standard for training, and inference for such a massive model (reminder: it’s an MoE with 671 billion parameters, 37 billion of which are active per token generation) requires a ton of hardware. Inference costs are roughly on par with a 70B dense model. Naturally, they’ll scale this success by throwing even more hardware at it and making the model bigger.&lt;/p&gt;

&lt;p&gt;Not to mention that DeepSeek makes LLMs more accessible for the on-prem customers. Which means smaller businesses will buy more GPUs, which is still good for NVDA, am I right?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Does this mean the model is bad? No, the model is very, VERY good.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It outperforms the vast majority of open-source models, which is fantastic. DeepSeek used 8-bit floating point numbers (FP8) throughout the entire training process. This sacrifices some of that precision to save memory and boost performance. Additionally, they employed a multi-token prediction system and innovative GPU clustering/connectivity techniques. These are clever and practical engineering choices that undoubtedly contributed to their success.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, though, stocks will recover, ideas will spread, models will get better, and progress will march on (hopefully).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>deepseek</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>I really wanted to like Tailwind CSS</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Tue, 06 Dec 2022 22:11:48 +0000</pubDate>
      <link>https://dev.to/alexjitbit/i-really-wanted-to-like-tailwind-css-29f5</link>
      <guid>https://dev.to/alexjitbit/i-really-wanted-to-like-tailwind-css-29f5</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Nobody:&lt;/p&gt;

&lt;p&gt;Absolutely no one:&lt;/p&gt;

&lt;p&gt;Me: Here's what I think about Tailwind CSS!&lt;/p&gt;




&lt;h2&gt;
  
  
  First - a hat tip
&lt;/h2&gt;

&lt;p&gt;Let's get something out of the way: Tailwind CSS &lt;em&gt;is&lt;/em&gt; great.&lt;/p&gt;

&lt;p&gt;For starters, Tailwind is a very polished and well thought &lt;em&gt;product&lt;/em&gt;. As a fellow bootstrapper - I tip my hat. The docs are amazing, the examples are great, Tailwind UI Kit is a life saver, the "Refactoring UI" book is a must read. And I'm an extremely happy paying customer for Adam's &amp;amp; Steve's stuff.&lt;/p&gt;

&lt;p&gt;Tailwind is great as a tool too - it's a perfect UI builder for &lt;em&gt;new projects&lt;/em&gt;. Creating stuff from scratch with Tailwind is just ah-amazing. Heck, I have actually just redesigned &lt;a href="https://www.jitbit.com/alexblog/tailwind/"&gt;this very website&lt;/a&gt; using Tailwind!&lt;/p&gt;

&lt;p&gt;Think of it as a visual editor - "Figma for developers" - a graphics design tool without leaving your code editor. Playing with default classes, trying stuff, re-trying stuff, then throwing in some more stuff... Hey, even Tailwind's landing page advertises this very approach in their hero video. You start with a bunch of unstyled mess and work from there. Sounds grrreat.&lt;/p&gt;

&lt;p&gt;And those are exactly the two main reasons developers loooooove Tailwind: because (A) - developers love writing and rewriting everything &lt;em&gt;from scratch&lt;/em&gt; (oh yeah, writing code is so much easier then reading code). And (B) - developers love doing stuff without leaving their code editors. And also, maybe (C) - developers tend to think about UI at the last minute. "Hey, I just hacked a cool project, now I need some UI for it - what can I use to turn this messy Times-New-Roman ugliness into something decent, fast?"&lt;/p&gt;

&lt;p&gt;I get it, not everyone is an experienced front-end dev (I'm surely not one!), who loves polishing and re-polishing the UI, pixel by pixel, color shade by color shade... Screw that, just give us a flexible system with some nice-looking defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  However...
&lt;/h2&gt;

&lt;p&gt;Looking at what Tailwind has grown into by version 3.2, my concerns against using Tailwind in big projects are:&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind takes up 70% of my markup...&lt;br&gt;...even after I'm done with the design!
&lt;/h2&gt;

&lt;p&gt;While Tailwind makes writing CSS fast and enjoyable, the biggest problem is that once I'm done with CSS - Tailwind is still in my face. Staring at me even after I'm done with the design. Making everything that's &lt;em&gt;not CSS&lt;/em&gt; - content, JS, markup etc - much harder to work with. It takes valuable space, and going back to a file that looks like this causes me physical pain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="min-h-full bg-white px-4 py-16 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8"&amp;gt;
  &amp;lt;div class="mx-auto max-w-max"&amp;gt;
    &amp;lt;main class="sm:flex"&amp;gt;
      &amp;lt;p class="text-4xl font-bold tracking-tight text-indigo-600 sm:text-5xl"&amp;gt;400&amp;lt;/p&amp;gt;
      &amp;lt;div class="sm:ml-6"&amp;gt;
        &amp;lt;div class="sm:border-l sm:border-gray-200 sm:pl-6"&amp;gt;
          &amp;lt;h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl"&amp;gt;Error&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6"&amp;gt;
          &amp;lt;a href="#" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"&amp;gt;Go back home&amp;lt;/a&amp;gt;
          &amp;lt;a href="#" class="inline-flex items-center rounded-md border border-transparent bg-indigo-100 px-4 py-2 text-sm font-medium text-indigo-700 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"&amp;gt;Contact support&amp;lt;/a&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I just wanted to change the "Error" header text, now where is that exactly? Do you see it?&lt;/p&gt;

&lt;p&gt;Look, Tailwind, I like you, we've had great time together, but now I'm done with the design, I'm moving on. Now stop staring at me and get out of my face.&lt;/p&gt;

&lt;p&gt;Now, this is not a huge problem when coding landing pages and websites like this one you're reading right now. Because marketing websites are 99% design and only 1% code (and all that 1% sits in your "contact us" form). But in a complex app with with hundreds of thousands LOC of business logic...&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintainability &amp;amp; discoverability
&lt;/h2&gt;

&lt;p&gt;Let's say you already have a huge app and you need to fix a minor UI bug. Some button looks odd or something.&lt;/p&gt;

&lt;p&gt;Since Tailwind is an abstraction over CSS, it &lt;strong&gt;adds extra steps to reverse engineering&lt;/strong&gt;. Which is what debugging essentially is - reverse engineering your own code. Working backwards from an unexpected result to the reason behind it.&lt;/p&gt;

&lt;p&gt;Say, you haven't touched your CSS in months. You can't just "remember" what might be wrong with that button. So you fire up your dev-tools in the browser, right-click the offender, "inspect element" and notice some strange styling that comes from... the compiled "output.css". But where the heck did it come from exactly? Is it a TW utility class we used on the element via &lt;code&gt;@apply&lt;/code&gt;? Is it the "forms" plugin? Is it the "prose" plugin? Did someone override the default "theme" in "tailwind.config.js"? Or may be it's the default "preflight" base styling that TW applies? And where is that configured exactly, the "tailwind.config.js"? NOPE, turns out our new developer has added a &lt;code&gt;@layer&lt;/code&gt; override in "input.css". OK, I think I found the reason, let's try the fix now! Opening my HTML, locating the element in a wall of utility classes, applyting the fix, done.&lt;/p&gt;

&lt;p&gt;Refreshing the browser. Wait, nothing changed. Was it the wrong fix? Oh, wait, maybe it's the npm-based "JIT" watcher that's not working? Let me see... Yep, it's down. The npm process has either crashed again with the "JavaScript heap out of memory" error, or maybe the build-script does not work on WSL2 because I'm currently on my Windows laptop... Anyway, let's just launch it manually (googling-googling-found it) &lt;code&gt;npx blah-bleh --WATCH&lt;/code&gt; thanks StackOverflow. I really hope the fix works, because I don't want to start over.&lt;/p&gt;

&lt;p&gt;By the way, did I mention that Tailwind CSS is &lt;em&gt;the only freaking reason&lt;/em&gt; we have &lt;code&gt;npm&lt;/code&gt; in this project? Yeah, not everyone uses JavaScript. People also use C#, Java, Rails, Go, Python and even (wait for it...) PHP. We're a dotnet shop, we already use nuget, libman, web-compiler, hell, why not throw npm into the mix. With "package.json", build scripts, a 13MB "node-modules" directory and all...&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrability
&lt;/h2&gt;

&lt;p&gt;Let's say you'd like to try to integrate Tailwind CSS into a big existing project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project is big. It has half a million lines of code in 4087 files. Around 500 of those are front-end markup files - HTML views and partials (ouch!)&lt;/li&gt;
&lt;li&gt;The project already has a theme + design system, also brand-colors, icons (ouch #2)&lt;/li&gt;
&lt;li&gt;...and a default font, visual hierarchy etc etc (ouch #3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You basically have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rewrite everything from scratch (of course)&lt;/li&gt;
&lt;li&gt;Gradual "incremental" rewrite, starting small and letting Tailwind take over eventually&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's try option 2. Say I'd like to redesign that ugly looking button. After all Tailwind is just a bunch of utility classes, should be no problem isolating it from the rest of the app, right?&lt;/p&gt;

&lt;p&gt;Installing Tailwind... done. The project looks awful, obviously, but that's fine. Tailwind has added its default font and a CSS normalizer. Let's override the default font back. Let's also make our existing CSS "Tailwind-friendly" so it does not conflict with all the default &lt;code&gt;box-sizing&lt;/code&gt;, &lt;code&gt;height:auto&lt;/code&gt;, margins, colors, let's add back the bold-font to all the H1-H6, remove default outlines etc.&lt;/p&gt;

&lt;p&gt;If that sounds like too much work we can always just disable TW "preflight" completely, but this way the utility classes might look weird.&lt;/p&gt;

&lt;p&gt;OK, we've added Tailwind, set up the tooling for all the devs (some use VSCode, some use Webstorm, some prefer the "big" Visual Studio), and after only 2 days our app looks more or less the same as before.&lt;/p&gt;

&lt;p&gt;Our project is not heavily "componentized", if we have a button in our code - then it is... just a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, not a partial or a component. Sometimes we might add &lt;code&gt;&amp;lt;button class="inactive"&amp;gt;&lt;/code&gt; but that's about it. I just ran a search, and we have 378 &lt;code&gt;&amp;lt;buttons&amp;gt;&lt;/code&gt; in our code. If I want them all to have rounded corners, I would either have to copy-paste &lt;code&gt;rounded-md&lt;/code&gt; 378 times, or convert all my buttons to a component/partial, which sounds like an overkill.&lt;/p&gt;

&lt;p&gt;Let's go with the &lt;code&gt;@apply&lt;/code&gt; directive then. That's exactly what it's for, according to the Tailwind docs "creating a partial for something as small as a button can feel like overkill". Exactly! I'll just rewrite all my buttons using &lt;code&gt;@apply&lt;/code&gt;. And now we have basically ended up with a CSS file (again) that uses TW-utilities instead of the "regular" CSS. We now have &lt;code&gt;button { @apply px-4 py-2 rounded-md }&lt;/code&gt; instead of &lt;code&gt;button { padding: x y; border-radius: z; }&lt;/code&gt;. Good. I don't get how is that better than regular CSS, tho. We still have an external CSS file, we still describe all our elements in it, just with a different syntax now. What have we gained? Aside from the Tailwind's constraints (in a good way) and the ability to use Tailwind UI kit that we paid for.&lt;/p&gt;

&lt;h2&gt;
  
  
  "You just don't get it, boomer"
&lt;/h2&gt;

&lt;p&gt;Yeah, I must be old. So I went out and searched Hacker News for all the TW-related comments and then went on and listened to 5 podcast episodes. These are the benefits developers mention all the time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Developers (apparently) hate having a separate CSS file&lt;/strong&gt; They really do, as it turns out. This is a surprise to me. But, well, OK, I'll take that as a valid reason. Kinda ruined if you use @apply tho.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Developers miss the native-app dev experience&lt;/strong&gt; a surprising number of people have literally mentioned this, including Adam Wathan himself. Coming from the native development world (writing desktop or mobile apps for Mac, PC, iOS etc) to the web - they miss the ability to simply click a UI element in the IDE (like a textbox) and then edit its properties in the side bar (bold font, black background etc). Without having to come up with an identifier or a class for the element. Without going to a separate css-file and target the identifier's selector. Without writing actual CSS. Without keeping cross-browser nuances in mind, along with the cascading nature and the "not repeat yourself" rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Devs hate naming stuff&lt;/strong&gt; and coming up with class names takes a significant part of their day. Everyone mentions that. A lot.&lt;/p&gt;

&lt;p&gt;All valid points, I guess. However, &lt;strong&gt;we don't write CSS every day&lt;/strong&gt;. I just looked at our LESS file (yeah, we still use LESS) and pulled up the git history for it. In the last 10 years (yeah, we're that old) it's been modified once every two months on average. See, our product is a long-lasting, profitable, boring app. We get back to CSS only when something breaks or something needs a facelift and that's it. We're OK with having it in a separate file, and coming up with a class name once every two months is not a huge problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind is an ORM
&lt;/h2&gt;

&lt;p&gt;While analyzing Tailwind's pros and cons I realized it kinda reminds me of another "tool" - an ORM. ORM libraries help with database access, so you don't have to "write SQL". They're are great to jump-start an app, build a landing page, a prototype, an MVP, to test a hypothesis, hey, even power up a working product with paying customers for a couple of years. But once your app gets mature and scales up to millions of daily active users - ORM quickly becomes a pain for an experienced DBA. You know, the type of DBA who sees a way to optimize a database query just by glancing at its execution plan XML. You're in deep waters now, you're in the land of database sharding and distributed caches. And now the ORM just gets in the way. Our DBA spends more time fighting it, instead of throwing away an abstraction layer and make things simple again.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;(or wait until someone releases a light weight, non-opinionated "micro" ORM, like "Dapper" from Marc, Nick and Sam - the guys who wrote that website we all use every day called StackOverflow)&lt;/small&gt;&lt;/p&gt;




&lt;p&gt;Coming up in the "grumpy old dev" series: how to build a static website without React + Next.js + build-step + Docker.&lt;/p&gt;

</description>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Global exception handling in ASP.NET Core</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Fri, 22 Oct 2021 20:07:00 +0000</pubDate>
      <link>https://dev.to/alexjitbit/global-exception-handling-in-aspnet-core-2dpm</link>
      <guid>https://dev.to/alexjitbit/global-exception-handling-in-aspnet-core-2dpm</guid>
      <description>&lt;p&gt;One of the (many) annoyances we bumped into when rewriting our (huge) app from .NET Framework to .NET Core was the inability to handle exceptions globally both via an error page &lt;em&gt;and&lt;/em&gt; some exception handling code.&lt;/p&gt;

&lt;p&gt;See, .NET 5 has &lt;code&gt;app.UseExceptionHandler()&lt;/code&gt; and the way you use it is either via error page (and extracting the actual error object on that page):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/Error"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//error handling page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OR use the exception handling lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorApp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;//error handling lambda&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;errorApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;exceptionHandlerPathFeature&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IExceptionHandlerPathFeature&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exceptionHandlerPathFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;//do something with the exception&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you want to do both - show an error page &lt;em&gt;and&lt;/em&gt; use a lambda to process the exception (for example, to send an error email)? Preferably without the bulky &lt;code&gt;context.Features.Get&amp;lt;ExceptionHandlerPathFeature&amp;gt;&lt;/code&gt;? This is how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/Error"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;//add middleware&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//run next middleware code&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="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//do something&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//re-throw so it's caught by the upper "/Error"&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;



</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>System.Drawing vs SkiaSharp benchmark</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Fri, 16 Apr 2021 08:56:12 +0000</pubDate>
      <link>https://dev.to/alexjitbit/system-drawing-vs-skiasharp-benchmark-30p5</link>
      <guid>https://dev.to/alexjitbit/system-drawing-vs-skiasharp-benchmark-30p5</guid>
      <description>&lt;p&gt;This is a ridiculously short post for .NET devs looking to compare &lt;code&gt;System.Drawing&lt;/code&gt; with &lt;code&gt;SkiaSharp&lt;/code&gt; (the fastest open-source alternative).&lt;/p&gt;

&lt;p&gt;I'm currently building a new pet project - a free tool, that will allow bloggers and webmasters generate open-graph "cover images" via sending GET requests to a simple API and then hot-linking images right into their pages. That is why I was in need of a fast, simple and scalable image processing library for .NET:&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%2Fi.imgur.com%2FLWaESLr.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%2Fi.imgur.com%2FLWaESLr.jpg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those unaware, &lt;code&gt;System.Drawing&lt;/code&gt; is an image manipulation and generation tool that is part of .NET Framework. But since it depends on Windows so much - it was not included in .NET Core. That is why developers mostly use &lt;code&gt;ImageSharp&lt;/code&gt; and &lt;code&gt;SkiaSharp&lt;/code&gt; (SkiaSharp is faster but comes with a C++ library, ImageSharp is slower, but it's 100% managed code).&lt;/p&gt;

&lt;p&gt;Now that &lt;a href="https://www.nuget.org/packages/System.Drawing.Common/" rel="noopener noreferrer"&gt;System.Drawing.Common&lt;/a&gt; is finally available for .NET Core and is even &lt;em&gt;cross-platform&lt;/em&gt;, it's time to give it a try.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;System.Drawing&lt;/code&gt; gets a lot of hate. There are dozens of posts why you shouldn't be using it: high CPU load, concurrency issues, slow performance etc. I decided to test that last one and benchmark it against the fastest alternative - SkiaSharp. It is based on Google Skia, a portable image manipulation API from the big G. The code generates a 120x80 thumbnail from a 500kb picture. Here are the results:&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET Framework
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// * Summary *

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
  [Host]     : .NET Framework 4.8 (4.8.4341.0), X86 LegacyJIT
  Job-YOWEFT : .NET Framework 4.8 (4.8.4341.0), X86 LegacyJIT

IterationCount=10  LaunchCount=1  WarmupCount=1

|                       Method |     Mean |     Error |    StdDev |
|----------------------------- |---------:|----------:|----------:|
| CreateThumbnailSystemDrawing | 6.733 ms | 0.3119 ms | 0.1856 ms |
|     CreateThumbnailSkiaSharp | 7.421 ms | 0.1057 ms | 0.0629 ms |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see System.Drawing is faster, at least on Windows. I tried it with different images, played around with different settings to make SkiaSharp faster (with or without anti-aliasing, different interpolation techniques etc.) and the results were consistent: System.Drawing was always better.&lt;/p&gt;

&lt;p&gt;But may be it's just .NET Framework using some unfair tricks? Let's test on .NET Core&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET Core 5.0
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// * Summary *

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.201
  [Host]     : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT
  Job-IFFNJZ : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT

IterationCount=10  LaunchCount=1  WarmupCount=1

|                       Method |     Mean |     Error |    StdDev |
|----------------------------- |---------:|----------:|----------:|
| CreateThumbnailSystemDrawing | 5.458 ms | 0.2266 ms | 0.1499 ms |
|     CreateThumbnailSkiaSharp | 7.325 ms | 0.9527 ms | 0.6302 ms |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, that's even a bigger difference. Almost 1.5x faster than SkiaSharp. The old dog is still kicking.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Originally published &lt;a href="https://www.jitbit.com/alexblog/300-systemdrawing-vs-skiasharp-benchmark/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building and minifying JS, CSS, LESS, SASS in Visual Studio with no extensions</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Sun, 22 Nov 2020 17:47:44 +0000</pubDate>
      <link>https://dev.to/alexjitbit/building-and-minifying-js-css-less-sass-in-visual-studio-with-no-extensions-k55</link>
      <guid>https://dev.to/alexjitbit/building-and-minifying-js-css-less-sass-in-visual-studio-with-no-extensions-k55</guid>
      <description>&lt;p&gt;Developing an ASP.NET app (Core or Framework/MVC) still requires working with external web tools sometimes - for example, when you want to use advanced web-dev stuff like SASS/LESS, PostCSS, or modern frameworks like Tailwind CSS, or just simply being able to minify, bundle and "babelize" your JavaScript&lt;/p&gt;

&lt;p&gt;Folks from the .NET camp are not that familiar with the &lt;code&gt;npm&lt;/code&gt; world and usually turned to "VS extensions" for these tasks. Like "Web Essentials" or "Web Compiler" or similar.&lt;/p&gt;

&lt;p&gt;The problem with that - those web extensions become abandoned, buggy, or even incompatible with recent versions of Visual Studio. So at some point .NET developers realize they &lt;em&gt;have&lt;/em&gt; to turn to "native" web package management and task runners. And to be frank, ASP.NET folks (including myself not so long ago) are not that experienced with the modern web/devops stack and it can be overwhelming to learn so many new things at the same time. That is why I decided to write this.&lt;/p&gt;

&lt;h2&gt;
  
  
  It all started with an abandoned extension
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.WebCompiler"&gt;Web Compiler&lt;/a&gt; used to be a great tool for Visual Studio. The only problem - it is now &lt;a href="https://github.com/madskristensen/WebCompiler/issues/468"&gt;abandoned&lt;/a&gt;, even though it's been created inside Microsoft. JS minification is not always working, builds are failing, file mapping is buggy, etc. As of today, the repo contains 209 unfixed issues.&lt;/p&gt;

&lt;p&gt;But we still need to minify JS and compile our CSS from LESS, do we? Turns out, most of the alternative solutions - "WebPack Runner" extension, "NPM Task Runner", "Bundler and Minifier", "Web Essentials", etc - are also &lt;a href="https://github.com/madskristensen/NpmTaskRunner/issues/80"&gt;abandoned&lt;/a&gt;. They were all created by the &lt;em&gt;same guy&lt;/em&gt; - Mads Kristensen - who works for Microsoft and created many great things, but MS clearly has other plans for him these days.&lt;/p&gt;

&lt;h2&gt;
  
  
  How about native VS without any extensions?
&lt;/h2&gt;

&lt;p&gt;Thing is, Visual Studio - even the free "Community Edition" - comes with built-in support for &lt;code&gt;npm&lt;/code&gt;, and a built-in "Task Runner Explorer" tool (that used to be an extension too), and this runner supports Grunt and Gulp out of the box.&lt;/p&gt;

&lt;p&gt;Which means we can configure a project in a way that everything works in a "naked" fashion without any external tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step example: minifying JS automatically
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; First important step is to enable this setting in Visual Studio: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1oM-ky-Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/8hJMovn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1oM-ky-Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/8hJMovn.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will ensure that NPM-modules are updated automatically every time you open the solution or edit &lt;code&gt;package.json&lt;/code&gt; file, similar to what Nuget does in the background. So we don't have to run &lt;code&gt;npm install&lt;/code&gt; CLI every time we add/update something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Create a &lt;code&gt;package.json&lt;/code&gt; file in the project root. You can do this via VS menu item: "Add - New item - npm configuration file" menu (type "npm" into the search box for faster navigation): &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kx8dUpZH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/hVh6u0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kx8dUpZH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/hVh6u0s.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This file is the starting point for NPM - it lists all the node modules you'll be running. Similar to &lt;code&gt;packages.config&lt;/code&gt; in Nuget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; In &lt;code&gt;package.json&lt;/code&gt; add "grunt", "grunt-contrib-uglify" and "grunt-contrib-watch" packages (or go with Gulp if you prefer). The nice thing is that VS will kindly suggest package names and versions as you type: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BJcNgx19--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/TT9M7iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJcNgx19--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/TT9M7iy.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what you should get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "version": "1.0.0",
 "name": "asp.net",
 "private": true,
 "devDependencies": {
  "grunt": "*",
  "grunt-contrib-uglify": "*",
  "grunt-contrib-watch": "*"
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've jsut installed three modules: Grunt plus the two plugins - "minifier" and "watcher". The former minifies a file, the latter "wacthes" a file for changes.&lt;/p&gt;

&lt;p&gt;Once you save that file, Visual Studio will automatically download and install all npm packages and all their dependencies in the background. It all goes into the "/node_modules" project subdirectory, so make sure it's excluded in your .gitignore file ;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Now that Grunt is installed it's time to configure it. Create &lt;code&gt;Gruntfile.js&lt;/code&gt; via "Add - New item - Grunt config file" &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p4OxfToS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/vMRoacr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p4OxfToS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/vMRoacr.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio will create a default Gruntfile with no tasks. Add the following lines after "initConfig":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;binding ProjectOpened='watch:tasks' /&amp;gt;
/*
This file in the main entry point for defining grunt tasks and using grunt plugins.

Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&amp;amp;clcid=0x409
*/

module.exports = function (grunt) {
    grunt.initConfig({
        uglify: { //minify task
            my_target: {
                files: {
                    'js/destination.min.js': 'js/source.js'
                }
            }
        },
        watch: { //watching these files for changes
            files: ['js/source.js'],
            tasks: ['uglify']
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Open "View - Other Windows - Task Runner Explorer" and make sure the Grunt task is there. Hit the "refresh" icon if it's not. Try to run the "uglify" task and make sure it has created the minified JS file.&lt;/p&gt;

&lt;p&gt;Then right-click the "watch" task and bind it to the "Project Open" event. This way every time you open the project - Grunt will start "watching" the files you specified in yout Gruntfile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; That's it! The great thing about this "set and forget" setup, is that all your teammates will get this config after doing a simple &lt;code&gt;git pull&lt;/code&gt; and everything will just work. &lt;/p&gt;

&lt;p&gt;Hope the code snippets are clear without any explanations. This works for both ASP.NET MVC 5 and ASP.NET Core MVC. For further reading check this &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/client-side/using-grunt?view=aspnetcore-5.0"&gt;MS doc&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  P.S. Why use Grunt or Gulp and not just npm "scripts"?
&lt;/h2&gt;

&lt;p&gt;Npm has a built-in task runner, that goes into the "scripts" section of "package.json" and a lot of people consider this the best practice today, instead of using Grunt/Gulp. But there's no way to run those script without installing the "NPM Task Runner" extension that is also kinda abandoned. My challenge here was to use plain Visual Studio without any plugins.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>How long do you think this code will run?</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Tue, 17 Nov 2020 23:58:21 +0000</pubDate>
      <link>https://dev.to/alexjitbit/how-long-do-you-think-this-code-will-run-4pfd</link>
      <guid>https://dev.to/alexjitbit/how-long-do-you-think-this-code-will-run-4pfd</guid>
      <description>&lt;p&gt;We have just spent the last 24 hours fixing a nasty bug in production.&lt;/p&gt;

&lt;p&gt;But first, here's a tricky question for you: &lt;b&gt;for how long do you think this code will run?&lt;/b&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//I'm trying to be language agnostic here
for (i=0; i&amp;lt;1000000; i++)
   thread.sleep(1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would seem that the answer is obvious - a million milliseconds, which is about 15 minutes.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;WRONG.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The correct answer is - &lt;i&gt;more than 4 hours&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;Wait, what?! Bare with me ;-)&lt;/p&gt;

&lt;h2&gt;The nasty production bug story&lt;/h2&gt;

&lt;p&gt;We have a background worker on the backend - a huge &lt;code&gt;while&lt;/code&gt; loop that runs through an array of millions of elements and does all kinds of in-memory checks and manipulations on them.&lt;/p&gt;

&lt;p&gt;But we don’t want the CPU to get stuck at 100% during this loop and choke the server, do we? We want the server to stay alive and kicking.&lt;/p&gt;

&lt;p&gt;So what does the average Joe programmer do? That's right - Joe adds a short pause into the cycle and goes home.&lt;/p&gt;

&lt;blockquote&gt;if there's any game developers reading this - I can already hear you giggling and reaching out for popcorn&lt;/blockquote&gt;

&lt;p&gt;Here's the thing: "pauses" AKA &lt;code&gt;Delay()&lt;/code&gt; AKA &lt;code&gt;Sleep()&lt;/code&gt; in most operating systems are based on &lt;em&gt;timers&lt;/em&gt;. The resolution of these timers is 12-15ms. You cannot pause for 1 millisecond - there will be at least 15.&lt;/p&gt;

&lt;p&gt;So on a large array with a million elements, we get &lt;code&gt;15ms * 1000000 / 1000 / 60 / 60 = 4.16&lt;/code&gt; - more than four hours.&lt;/p&gt;

&lt;p&gt;And coming back to work in the morning our Joe-the-programmer sees what? That his loop is &lt;i&gt;still running&lt;/i&gt; from yesterday. The job, that used to take 7 minutes (although it kept the CPU at 100%), now takes half a day to finish. In a "relaxed" mode though.&lt;/p&gt;

&lt;p&gt;Everything is broken and customers start creating the "huh?!" tickets.&lt;/p&gt;

&lt;blockquote&gt;and the gamedevs reading this - laugh viciously&lt;/blockquote&gt;

&lt;p&gt;Because in their game development world this happens all the time, this is called a "busy loop" or a "tight loop". And you can't rely on timers.&lt;/p&gt;

&lt;h2&gt;But how do we throttle properly?&lt;/h2&gt;

&lt;p&gt;1. Use multimedia timers or timers from OpenGL/DirectX (overkill) &lt;/p&gt;

&lt;p&gt;2. Throttle every N-th step, not every step (inelegant and stinks)&lt;/p&gt;

&lt;p&gt;3. Dump "pauses" completely and use the magic &lt;code&gt;Thread.Yield&lt;/code&gt; instruction - which is a "polite" way to share resources and tell the operating system "hey, I'm still busy, but if you really need this, slow me down and let other threads do the work" (and this is the best way)&lt;/p&gt;

&lt;p&gt;The 100% CPU load won't go away, but now it's not an issue - everything is fast and responsive.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Thread.Yield&lt;/code&gt; is available in many languages:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;C#:&lt;/b&gt; &lt;code&gt;Thread.Yield&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;C++:&lt;/b&gt; &lt;code&gt;std::this_thread::yield&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Win32:&lt;/b&gt; &lt;code&gt;SwitchToThread&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Java:&lt;/b&gt; &lt;code&gt;Thread.yield&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Go:&lt;/b&gt; &lt;code&gt;runtime.Gosched&lt;/code&gt; (I think...)&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Visual Basic:&lt;/b&gt; &lt;code&gt;DoEvents&lt;/code&gt; (kidding! ...although not really)&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Python:&lt;/b&gt; &lt;code&gt;time.sleep(0)&lt;/code&gt; (on Windows it's &lt;code&gt;time.sleep(0.0001)&lt;/code&gt; don't ask me why... because Python...)&lt;/p&gt;

&lt;p&gt;(by the way, it's not just Python - quite a few system libraries are smart enough to translate &lt;code&gt;sleep(0)&lt;/code&gt; into a &lt;code&gt;yield&lt;/code&gt;, including .NET, Posix and WinApi)&lt;/p&gt;

&lt;p&gt;etc. Google your favorite language.&lt;/p&gt;

&lt;p&gt;The moral of the story, I guess, is that even the trivial things get super tricky &lt;i&gt;at scale&lt;/i&gt;. Things that used to be nice and simple when we just launched our little SaaS now get complicated when you have thousands of companies using your stuff. But that's a nice problem to have, I guess.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why Managers Make more Money than Engineers</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Wed, 11 Mar 2020 16:02:40 +0000</pubDate>
      <link>https://dev.to/alexjitbit/why-managers-make-more-money-than-engineers-ka3</link>
      <guid>https://dev.to/alexjitbit/why-managers-make-more-money-than-engineers-ka3</guid>
      <description>&lt;p&gt;Programmers often wonder, why executives make more money than developers. How come "John the manager" gets paid more than me, who's doing all the work?!&lt;/p&gt;

&lt;p&gt;Here's a little story I read, by Robert Sapolsky, a professor of biology at Stanford.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Naked Mole Rat
&lt;/h2&gt;

&lt;p&gt;There's this weird African specie, a rodent called "the naked mole rat", that lives in cooperative groups, mostly underground. While studying their behavior scientists soon began to realize there are two types of species in their colonies: the one that's skinny and hardworking, doing all the work ("the engineers"), the other types were lazy and fat ("the managers") and were just sitting around doing nothing all day, simply eating all the food brought by the hardworking types.&lt;/p&gt;

&lt;p&gt;It was really puzzling since it contradicted all known behavioral studies and theories. Until it turned out, that you just needed to watch the colony long enough to realize what's really going on.&lt;/p&gt;

&lt;p&gt;Turns out when the rainy season comes, these fat guys who have been sitting around doing nothing all year long eating tons of food - they go up, turn around and plug their butts into the tunnel entrances. Protecting everyone from snakes, coyotes &lt;em&gt;and&lt;/em&gt; preventing water from flooding the tunnels.&lt;/p&gt;

&lt;p&gt;Literally covering everyone's asses with their own.&lt;/p&gt;

&lt;p&gt;P.S. Here's &lt;a href="https://youtu.be/Y0Oa4Lp5fLE?t=4324"&gt;a video&lt;/a&gt; of Robert's lecture (link points to the timecode where he tells a version of this story)&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Back to PC after 14 years on a MacBook</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Wed, 15 Jan 2020 22:08:06 +0000</pubDate>
      <link>https://dev.to/alexjitbit/back-to-pc-after-14-years-on-a-macbook-k1g</link>
      <guid>https://dev.to/alexjitbit/back-to-pc-after-14-years-on-a-macbook-k1g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;(repost of my original essay from &lt;a href="https://www.jitbit.com/alexblog/277-back-to-pc-after-14-years-on-a-macbook/" rel="noopener noreferrer"&gt;here&lt;/a&gt; )&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been a happy MacBook user for almost 14 years. Tried all of them - from a tiny 11" MacBook Air to the enormous 17" Pro - and in 2014 I finally ended up with the 15-inch retina model.&lt;/p&gt;

&lt;p&gt;The best laptop I ever owned.&lt;/p&gt;

&lt;p&gt;Even if you need to occasionally run Windows for work - MBP is still the best possible hardware to do that. The 15-inch retina MacBook had that unique blend or elegance, power, durability... And by "durability" I mean I even fell off a motorcycle with this thing in my backpack. Twice.&lt;/p&gt;

&lt;p&gt;But.&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%2Fi.imgur.com%2F0Ul4ODA.jpg%3F1" 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%2Fi.imgur.com%2F0Ul4ODA.jpg%3F1" alt="keyboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my 5 y.o. laptop's keyboard. You can tell, it was time for me to move on. Although the thing still worked brilliantly and even (kinda) ran modern video games like PUBG or Battlefield.&lt;/p&gt;

&lt;p&gt;I kept upgrading this machine to the bitter end, replacing nearly everyting - the screen, the battery, the hard-drive, RAM, you name it - but it was just postponing the obvious. It was clearly time for an upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  And that's when I found myself in that position of "nowhere to upgrade to".
&lt;/h3&gt;

&lt;p&gt;Apple still sells the old 2015 model, but it's limited to the integrated Intel GPU. And the new Mac Book Pro is not an option, because... Well, leaving out the butterfly keyboard issues and the "usb-c-gate", it's mainly because&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;...the touch bar sucks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This thing requires you to &lt;em&gt;take your eyes off the screen&lt;/em&gt;, you can't just feel the "Esc" button with your finger. And what do you get in return? Not much. This bar is a solution in desperate search of a problem.&lt;/p&gt;

&lt;p&gt;If there was a 15" model without it - I'd buy it without blinking an eye.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally a candidate
&lt;/h2&gt;

&lt;p&gt;It took me more than &lt;em&gt;two years&lt;/em&gt; (partly because I loved my old MacBook too much and was simply postponing the decision) to eventually find a suitable replacement. After reading tons of reviews and even test-driving some of the options, my search for a great Mac Book replacement is finally over. I bought a new laptop and I'm loving it.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;Microsoft Surface Book 2&lt;/strong&gt;&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%2Fi.imgur.com%2F0QUtGxU.jpg%3F1" 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%2Fi.imgur.com%2F0QUtGxU.jpg%3F1" alt="Surface Book"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ironically, though, Apple still plays a huge role in why this laptop is so good. Apple has paved the road for everyone else. Apple has raised the bar, set the new standards and pointed the direction. So thank you, Apple, for making this Surface Book so good.&lt;/p&gt;

&lt;p&gt;See, PC manufacturers listened and learned their lesson. PC laptops are not those squeaky awkward pieces of crappy plastic covered with idiotic "Intel inside/Powered by Windows" stickers, as I remember them. Nope, they're all stylish-metal-unibody-PorscheDesign-whatever these days. And it's because of Apple.&lt;/p&gt;

&lt;p&gt;And while Microsoft keeps getting bad press for stealing so many good ideas from Apple, &lt;strong&gt;(a)&lt;/strong&gt; as a consumer I simply don't care, I just want the best from both worlds. And &lt;strong&gt;(b)&lt;/strong&gt; not only Surface Book 2 matches Apple's design standards, it goes far beyond.&lt;/p&gt;

&lt;h3&gt;
  
  
  Display
&lt;/h3&gt;

&lt;p&gt;This is one of the best screens in the industry, period. Being a super meticulous customer, I actually brought my old Mac &lt;em&gt;and&lt;/em&gt; my wife's new 2018 MacBook Pro with me to the Microsoft Store and compared them side by side. Microsoft won. Rich colors, more brightness even for a sunny day, great design, and as it turns out, the 2:3 aspect ratio is actually cool, giving you more vertical real estate.&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%2Fi.imgur.com%2FkVRdjOX.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%2Fi.imgur.com%2FkVRdjOX.jpg" alt="Screens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check this out. The picture quality is equally good on both. MS Surface (on the right) looks weirdly brighter and... blue-ish I guess? But that's just the wrong color profile. Calibration sorted that out.&lt;/p&gt;

&lt;p&gt;There's one tiny downside though. Both screens are glossy, but MS'es is somehow "glossier". I don't know if it's just me, but reflections seem tiny (!) bit brighter on a sunny day, when the sun's behind you. Probably because Mac's screen has some polarized spraying on it maybe? Or maybe my Mac is just too old and dirty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Magnesium body
&lt;/h3&gt;

&lt;p&gt;Not only it looks clean and beautiful, but it also feels very soft. It never gets too cold to touch (unlike MacBook) and stays clean forever. The hinge looks weird, but in a good way - adding character and personality to the laptop. I recently came back from Microconf, and three or four people at the conference had approached me with a "how do you like it?" question after spotting the model by its looks from afar.&lt;/p&gt;

&lt;p&gt;The keyboard is great - mainly because it's basically an exact replica of MBP's, with the layout, keys and gaps stolen from Apple, so your muscle-memory doesn't have to adapt (I'm wondering if that was intentional). The track pad - which has always been a huge problem on &lt;em&gt;every&lt;/em&gt; PC laptop I tried - finally works. Just works. It's invisible. Even to a 14-year Mac user like me. Microsoft has finally nailed it, both software and hardware wise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tablet mode
&lt;/h3&gt;

&lt;p&gt;Who would want that? Why would a laptop be a "two in one" device? I was super skeptical about the whole "detachable screen" concept, until after I tried it myself.&lt;/p&gt;

&lt;p&gt;First of all, this is a huge bonus for someone who travels a lot: you get a free tablet without bringing any extra devices. And it's not just any tablet - it's an actual, uhm, "computer" - with VSCode, terminal, SSH and all - and with an i7 quad core CPU inside it (since most of the laptop's computing power sits in the screen, not the base). You can write code behind your desk, then detach the screen and read a book or watch Netflix before falling asleep.&lt;/p&gt;

&lt;p&gt;By the way the tablet is also &lt;em&gt;super lightweight&lt;/em&gt; - which is something most reviewers don't mention for some reason. Actually, the whole machine is 15% lighter than my 15" MBP, which was a pleasant surprise.&lt;/p&gt;

&lt;p&gt;Believe it or not the tablet mode is so good, I came back to MS Store to buy the pen next day. A Microsoft hater like me! And the whole "pen" experience came out surprisingly good. Even though the thing is a little overpriced and should come included with the device if you ask me.&lt;/p&gt;

&lt;p&gt;But that wasn't the biggest surprise. After a while I found myself using the touch screen a lot &lt;em&gt;when in laptop mode&lt;/em&gt;. What seemed like a completely useless toy at first, turned out very natural, I find myself scrolling, zooming, resizing and drag-n-dropping shit all the time.&lt;/p&gt;

&lt;p&gt;(It really is the MPB's touch bar that seems like the definition of a "useless toy" to me...)&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;I know. "Windows". Meh. But just like PC laptops are not the crappy plastic junk from the old days, Windows has gotten better too. You probably havent' touched Windows for years, right? Just like most of my tech friends, who just don't understand how far Windows 10 has come. At everything: from touch pad gestures to the "Hello face" authentication (pretty similar to iOS'es "Face ID" unlock). Now imagine it all being finally coupled with "native" Microsoft hardware - at last! No more driver headaches, no more "rolling back that nVidia thing cause it conflicts with that other thing". Even the laptop's firmware comes via Windows Update now, just like Apple, I know ;-)&lt;/p&gt;

&lt;p&gt;If you're coming from MacOS/Linux though, here are the two hints that'll make you life much easier under Windows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install "Chocolatey"&lt;/strong&gt; which is a "Homebrew for Windows". Yes, Windows finally has a proper(ish) package manager. No more "download, run installer, click 'next-next-next', rinse, repeat". The syntax is pretty similar to apt-get and you can basically install everything you need via the command line. VSCode (or Sublime), git, Node, Python, your favorite browser, OpenVPN client, you name it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Ubuntu&lt;/strong&gt; from the "Store" app. It's a fully featured "linux subsystem" running inside Windows that lets you, say, set up a Ruby on Rails environment. Or Python/Django. Or Node. Or Nginx/Apache + Laravel/PHP + MySQL. Or whatever. Then just browse it at localhost:XXXX from Windows, no extra steps needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's gonna feel awkward at first, since some of the tools you use (like VSCode) run natively on Windows, while other run inside the linux terminal, but once you get used to accessing your windows-files via "/mnt/c/" from linux (which is how Ubuntu mounts your "C:" drive) - it gets easier from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Usually posts like this end with an Amazon affiliate "buy here" link :-) I'm not paid by MS nor making nay money... I really did spent two years researching and test driving MacBooks, Razer Blades, ThinkPads, Dells and whatnot.&lt;/p&gt;

&lt;p&gt;Microsoft Surface Book 2 is beyond great. Several years ago if you told me Microsoft would build a machine that is way ahead of the rest of the industry (including Apple), I'd laugh in your face. But they somehow managed to build a serious, powerful, work-oriented and elegantly designed notebook for professionals. It's equally good for "heavy" tasks, like debugging code, training an ML model, rendering a video, playing a modern game - &lt;em&gt;and&lt;/em&gt; - taking quick notes at a conference, sketching a mockup with the "pen" or watching "Silicon Valley" on a plane.&lt;/p&gt;

&lt;p&gt;There are dozens of little nice things and nuances I haven't even mentioned, but they add up to complete the whole experience. Like the magnetic charging port (MagSafe-inspired, I know), two "traditional" USB ports &lt;em&gt;and&lt;/em&gt; USB-C, the "reverse screen" mode, the toggleable "FN" key, the built-in Mail/Calendar apps, hand-writing recognition, long battery life, an extra USB charging port on the power adapter (loving it) and, of course, the GTX 1060 beast.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;only&lt;/em&gt; downside I can currently think of - and it's a bit of stretch - is the extremely &lt;a href="https://www.ifixit.com/Teardown/Microsoft+Surface+Book+2+Teardown/100364" rel="noopener noreferrer"&gt;low "iFixit" score&lt;/a&gt; for this machine. As a computer nerd, I would love the ability to open it up easily and, say, upgrade memory to 32 GB or replace thermal paste on the CPU, but that's just me being picky I guess. We'll see how it goes in a few years.&lt;/p&gt;

&lt;p&gt;Other than that it's a perfect machine for my development work, travel and gaming. There's no way I'm going back to a MacBook any time soon.&lt;/p&gt;

&lt;p&gt;UPDATE: I received, like, 10 DMs on Twitter questioning my motocycle riding skills, hahah. Just to clarify: I'm an enduro/offroad/dirtbike guy. 2 falls is nothing in our world.&lt;/p&gt;

</description>
      <category>mac</category>
      <category>windows</category>
      <category>productivity</category>
      <category>laptop</category>
    </item>
    <item>
      <title>Invalidating ASP.NET Forms Authentication tickets server-side</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Mon, 08 Jul 2019 19:55:39 +0000</pubDate>
      <link>https://dev.to/alexjitbit/invalidating-asp-net-forms-authentication-tickets-server-side-38n7</link>
      <guid>https://dev.to/alexjitbit/invalidating-asp-net-forms-authentication-tickets-server-side-38n7</guid>
      <description>&lt;p&gt;Sometimes you need to "log out other user sessions". To prevent cookie replay attacks or - a very common use case - log out other sessions when a user changes their password. ASP.NET does not have a built-in way of doing this, but there's a simple solution.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;FormsAuthenticationTicket&lt;/code&gt; object has a built-in property called &lt;code&gt;IssueDate&lt;/code&gt;. So you can easily invalidate all forms-auth tickets "older than date X". In our case, it would be "older than last password change"&lt;/p&gt;

&lt;p&gt;You can, for example, read the &lt;code&gt;IssueDate&lt;/code&gt; property inside &lt;code&gt;Application_AcquireRequestState&lt;/code&gt; (in "global.asax") and if the date is "too old" (i.e. older that the user's last password change) log the user out.&lt;/p&gt;

&lt;p&gt;Here's some code for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Application_AcquireRequestState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//check if token should be invalidated&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lastPswChange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetPswChangeDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;HttpCookie&lt;/span&gt; &lt;span class="n"&gt;authCookie&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FormsAuthentication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormsCookieName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;FormsAuthenticationTicket&lt;/span&gt; &lt;span class="n"&gt;authTicket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FormsAuthentication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authCookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//psw changed since this auth-token has been issued&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authTicket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IssueDate&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;lastPswChange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//log him out&lt;/span&gt;
            &lt;span class="nf"&gt;Logout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"~/User/Login"&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Abandon&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;FormsAuthentication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SignOut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will have to implement the GetPswChangeDate method yourself.&lt;/p&gt;

&lt;p&gt;"Password change date" is just one example. You can have and other date saved in your database next to every user and set it explicitly to whatever value you'd like.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Legal Stuff They Don't Tell You About Remote Work</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Fri, 28 Jun 2019 15:27:37 +0000</pubDate>
      <link>https://dev.to/alexjitbit/legal-stuff-they-don-t-tell-you-about-remote-work-of9</link>
      <guid>https://dev.to/alexjitbit/legal-stuff-they-don-t-tell-you-about-remote-work-of9</guid>
      <description>&lt;p&gt;My company - &lt;a href="https://www.jitbit.com/"&gt;Jitbit&lt;/a&gt; - is an all remote company. We are 100% distributed, we don't have an office and our teammates literally work from around the globe (currently it's the UK, the US, Latvia, Israel and Turkey).&lt;/p&gt;

&lt;p&gt;So it might seem strange for me to warn people against remote work. But let's have a look from the &lt;em&gt;employee's angle&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is what you need to know when you're about to work for someone remotely and internationally.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  You're a CONTRACTOR now
&lt;/h2&gt;

&lt;p&gt;This part is crucial and all the other points more or less come out of this one, so let me stress every sentence in this paragraph:&lt;/p&gt;

&lt;p&gt;You're not officially employed any more. Legally you're &lt;em&gt;self-employed&lt;/em&gt; now.&lt;/p&gt;

&lt;p&gt;You're not protected by employment law and/or your country's labor authorities.&lt;/p&gt;

&lt;p&gt;"Employee rights", benefits, labor unions, regulations - do not apply to you anymore.&lt;/p&gt;

&lt;p&gt;You're on your own.&lt;/p&gt;

&lt;p&gt;The only thing that regulates your work is &lt;em&gt;your contract&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your CONTRACT is super important
&lt;/h2&gt;

&lt;p&gt;The above mentioned issues make your contract crucial. It is the only thing that protects you. Once you become a contractor - as opposed to an employee - your country's &lt;em&gt;employment law&lt;/em&gt; (very strict in most European countries) just stops working. No more minimum wages, no wrongful terminations, your new employer can fire you any moment and get away with that.&lt;/p&gt;

&lt;p&gt;This is why your contract needs to cover &lt;em&gt;everything&lt;/em&gt;, even the parts that seemed "obvious" when you were working in the office. Everything: employee benefits, medical insurance costs, layoff procedures, holidays and vacations, promotions and - of course - compensation. Lawyer up! (just kidding... although, it's never a bad idea to have legal help).&lt;/p&gt;

&lt;h2&gt;
  
  
  You probably need to register as "self-employed", "sole proprietor", "individual entrepreneur" or similar
&lt;/h2&gt;

&lt;p&gt;In most countries it's illegal to perform "business activities" without actually registering a "business". And since you're now a contractor doing business with the remote company, you will need a &lt;em&gt;legal entity&lt;/em&gt;. There's no need to register a whole big ass company, since most countries offer "self employed" registration of some sort. Sometimes called "sole proprietorship", "individual entrepreneur" or a simpler "self-employed" form (although registering an actual company might be a good idea, more on that later). This "entity" will then sign the contract with your employer, receive the money and pay taxes.&lt;/p&gt;

&lt;p&gt;You will also need a bank account for this entity (obviously). And in some rare cases your bank, your country's tax office or even the company your work for might also ask you to issue an &lt;em&gt;invoice&lt;/em&gt; for every monthly payment your receive. Don't let this freak you out, this is totally normal, and the invoice will just mention "Monthly web-development services for June 2019 - q-ty: 1" or something, nothing too complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exception #1 - Authorship
&lt;/h2&gt;

&lt;p&gt;In some cases you can get away without registering a business and get your paycheck as an "authorship royalty". Like, you're the "author" of the source code, who "licenses" that code - to the remote company. And the company pays you "royalty". This way you're not in a "buyer-seller" relationship, you're in an "author-publisher" relationship, and this usually does not require registering a legal entity. But this might involve more paperwork and more lawyers (duh).&lt;/p&gt;

&lt;h2&gt;
  
  
  Exception #2 - PEO
&lt;/h2&gt;

&lt;p&gt;Another way to be hired remotely without registering any business is through an "employer of record" service, which acts as a proxy between the remote company and an employee such as yourself. Here's how it works: a global company like ShieldGeo (not affiliated) which has local legal entities in many many different countries, becomes the official "employer of record" for tax purposes and paperwork only. This way you are officially employed and can simply skip the rest of this article. Ask the remote company if they use a service like this (called "professional employer organization" - PEO) or even suggest using one - why not!&lt;/p&gt;

&lt;h2&gt;
  
  
  You're doing your own taxes now *
&lt;/h2&gt;

&lt;p&gt;In many countries taxes are "done for you" by the employer and companies usually publish job descriptions with "after tax" salaries in those countries. Employees sometimes do not even know how much would they earn "before tax" and in some countries (believe it or not) people are not even aware of the current tax rates.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[Political nerd mode on] Actually there's a pretty clear correlation between how "free" a country is, and whether people do their own taxes or not. Turns out, countries where people know their taxes are more liberal and civilly active (because you actually see how much money you give away every month), while countries where taxes are "done for you" tend to be politically indifferent and often end up in some form of authoritarianism.[/off]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyways. If your taxes were done for you by the employer in the past, well, that's going to end. You will have to report to your Tax Office, file the tax return and actually pay taxes in the end.&lt;/p&gt;

&lt;p&gt;You will probably need an accountant too. They will advise you on how to save on taxes, which expenses are deductible, whether registering a company is better than "self-employment" (might also help save some taxes), and generally save you a lot of time and headache. It shouldn't be very expensive either, I pay my accountant approximately 100 Euro a month ($120).&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and other stuff to negotiate with your remote employer
&lt;/h2&gt;

&lt;p&gt;Here at Jitbit we cover: medical bills, insurance costs, some of the taxes (in countries where being "self-employed" results in bigger taxes than being employed "normally"), work expenses (like a new laptop allowance etc). We even provide 0-interest dateless loans to our employees - since we do realize that it's harder to get a mortgage when you're "self-employed". By the way, that's another reason to register a company, rather than "sole proprietorship", because "a company" always looks better for your bank (and your grandma too).&lt;/p&gt;

&lt;p&gt;Anyway, you will have to negotiate all that after getting the job offer. Don't be shy, it never hurts to ask. Also, their reaction is a good way to tell the good guys from shady outsourcers who simply want cheap workforce (and then they eff-up Hertz website ;).&lt;/p&gt;

&lt;p&gt;On the other hand, do remember, that this whole "remote" thing is still very new. Some companies are only trying these waters for the first time, they might simply not know what to offer and how to pay. Give us a chance, we're still learning.&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://open.buffer.com/international-pay/"&gt;How We Pay Remote Employees&lt;/a&gt; by Buffer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://about.gitlab.com/handbook/people-operations/global-compensation/"&gt;Global Compensation&lt;/a&gt; by Gitlab&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doist.com/blog/mental-health-and-remote-work/"&gt;What Most Remote Companies Don’t Tell You About Remote Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you know more good posts on the topic do let me know in the comments, I'll add the links.&lt;/p&gt;

</description>
      <category>career</category>
      <category>remote</category>
      <category>hiring</category>
    </item>
    <item>
      <title>Cool Regex performance hack for "not preceded with"</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Sun, 05 May 2019 18:47:50 +0000</pubDate>
      <link>https://dev.to/alexjitbit/cool-regex-performance-hack-for-not-preceded-with-1nh9</link>
      <guid>https://dev.to/alexjitbit/cool-regex-performance-hack-for-not-preceded-with-1nh9</guid>
      <description>&lt;p&gt;The first part of this post applies only if you run ASP.NET, you can simply skip to the regex hack if not interested, but I strongly recommend you read the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging ASP.NET CPU spikes
&lt;/h2&gt;

&lt;p&gt;About three weeks ago we started seeing unusual CPU load spikes on &lt;a href="https://www.jitbit.com/helpdesk/"&gt;our app's&lt;/a&gt; production server. We do have a &lt;a href="https://github.com/jitbit/cpu-analyzer"&gt;super useful tool&lt;/a&gt; (originally written by &lt;a href="https://samsaffron.com/"&gt;Sam Saffron&lt;/a&gt;, forked by us) showing the exact call stacks that use up all the CPU ticks&lt;/p&gt;

&lt;p&gt;But the problem is you have to "attach" this tool to the process at the exact moment the CPU load spikes up. And "catching" that moment can be... uhm... hard.&lt;/p&gt;

&lt;p&gt;Even though we've set up AWS to text us when CPU load gets higher than 75% and stays there for 5 consecutive minutes (a must have for anyone who runs a business-critical SaaS app by the way) the time it takes to run to my laptop, remote-login to the sever, get to the command-line, attach to the overloaded process etc etc... CPU load gets back to normal. Darn.&lt;/p&gt;

&lt;p&gt;Luckily my cofounder has written a short Windows .BAT script that does this automatically - if the CPU load gets higher than 75% the script finds the web-application process ID, launches the tool, attaches it to the PID and dumps the call-stack data to a text file (if you're going to use the script, change the "OurApplicationPool" to the actual user account that runs your app pool).&lt;/p&gt;

&lt;p&gt;Feel free to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Echo OFF

SET /A "MAXUSAGE=75"

For /F %%P in ('wmic cpu get loadpercentage ^| FINDSTR "[0-9]"') do (
echo %%P
    IF %%P GTR %MAXUSAGE% (
        for /f "tokens=1,2 delims= " %%A in ('tasklist /FI "USERNAME eq OurApplicationPool" /fo table /nh') do cpu-analyzer.exe %%B &amp;gt;&amp;gt; log_%date:~-4,4%%date:~-7,2%%date:~-10,2%.txt
    )
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we have simply set up a job in Windows Task Scheduler that runs every 5 minutes (I know, ugly, but we needed a quick and dirty solution).&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing Regex
&lt;/h2&gt;

&lt;p&gt;After some digging through the logs and call stacks it turned out most of the CPU ticks were spent on &lt;code&gt;Regex.Search&lt;/code&gt;. Our app uses A LOT of regular expressions both on the back and front ends. For example, when converting markdown and "BbCode" (used for internal storage) to "normal" HTML (used for UI and emails).&lt;/p&gt;

&lt;p&gt;Long story short, after hours of blindly trying different regex patterns, testing and benchmarking here's what I found:&lt;/p&gt;

&lt;p&gt;If you want to match a rule that goes "something is not preceded with an 'A'" don't ever use code like this: &lt;code&gt;(^|[^a])something&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first group basically says "it's either the beginning of the text OR not the letter A" and most people find more readable compared to a "regex lookbehind", including myself. But it turns out regex "lookbehinds" and "lookaheads" are almost 3-4 times faster than patterns involving "beginning/end of string" especially when working with &lt;em&gt;large&lt;/em&gt; stings.&lt;/p&gt;

&lt;p&gt;In our case replacing &lt;code&gt;(^|[^a])&lt;/code&gt; with an &lt;code&gt;(?&amp;lt;!a)&lt;/code&gt; (which literally means "not preceded with") has made our code 3 times faster. I wrote a quick benchmark test that does 10000 search-replace iterations in both .NET and JavaScript regex engines (we use both) and the results were:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(^|[^a])something&lt;/code&gt; - 1346ms (avg)&lt;br&gt;
&lt;code&gt;(?&amp;amp;lt;!a)something&lt;/code&gt; - 389ms (avg)&lt;/p&gt;

&lt;p&gt;Whoa, that's more than 3 times faster! Obviously, the longer the input text you have, the bigger the impact.&lt;/p&gt;

&lt;p&gt;Cool. But can we improve it even further? Is there a way to make the "not preceded with an A" rule even faster?&lt;/p&gt;

&lt;p&gt;Turns out there is! Unfortunately there are not many regex performance analysis on the net, so (again) I was on my own here... But fortunately I somehow remembered reading a &lt;a href="https://news.ycombinator.com/item?id=10282121"&gt;cool HackerNews post&lt;/a&gt; couple of years ago that suggests a great way to optimize rules like "not preceded with" or "not surrounded with" or "not ends with" and similar. Basically the trick goes like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;asomething|(something)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nice, huh? If you want to match "something not preceded with an A" simply match what you DON'T WANT and then ignore overall matches, looking only at the "Group 1"! The benchmarks:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(?&amp;lt;!a)something&lt;/code&gt; - 389ms (avg)&lt;br&gt;
&lt;code&gt;asomething|(something)&lt;/code&gt; - 16ms (avg)&lt;/p&gt;

&lt;p&gt;Holy sh*t! That's almost a hundred times faster than my original code!&lt;/p&gt;

</description>
      <category>regex</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Removing files from Mercurial history</title>
      <dc:creator>Alex Yumashev</dc:creator>
      <pubDate>Sat, 20 Apr 2019 16:30:22 +0000</pubDate>
      <link>https://dev.to/alexjitbit/removing-files-from-mercurial-history-1b15</link>
      <guid>https://dev.to/alexjitbit/removing-files-from-mercurial-history-1b15</guid>
      <description>&lt;p&gt;Sometimes you need to remove files from Mercurial completely, even from the history. For instance, if you have accidentally stored a sensitive file in the repo (some password, or an access key, or a code-signing certificate etc. etc.) or committed a huge binary. Here's how you remove it:&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the "Convert" extension
&lt;/h2&gt;

&lt;p&gt;You will need the &lt;code&gt;hg convert&lt;/code&gt; command. This command is normally used to convert an SVN/GIT repository to Mercurial, but it can also be used to "convert" &lt;strong&gt;from Mercurial to Mercurial&lt;/strong&gt; too.&lt;/p&gt;

&lt;p&gt;The cool thing about it is the &lt;code&gt;--filemap&lt;/code&gt; option that specifies which files should be included in the conversion process and which should not.&lt;/p&gt;

&lt;p&gt;Add this code to &lt;code&gt;.hgrc&lt;/code&gt; to enable the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[extensions]
hgext.convert=
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Steps to remove a file from hg
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Make sure all your teammates have pushed their local changes to the central repo (if any)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backup your repository&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a "map.txt" file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# this filemap is used to exclude specific files
exclude "subdir/filename1.ext"
exclude "subdir/filename2.ext"
exclude "subdir2"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hg convert --filemap map.txt c:/oldrepo c:/newrepo 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;NOTE: You have to use "forward-slash" in paths, even on Windows.&lt;/p&gt;

&lt;p&gt;Then wait and be patient. After a while you will have a new repo at &lt;code&gt;c:\newrepo&lt;/code&gt; just without the files&lt;/p&gt;

&lt;h2&gt;
  
  
  (optional) Pushing your new repo back to central repo (e.g. Bitbucket)
&lt;/h2&gt;

&lt;p&gt;If you have some central hg-storage you will have to "strip" all your changesets and then "push" your local repostiroy again.&lt;/p&gt;

&lt;p&gt;For instance, if you use Bitbucket like us, go to "Admin - Strip". Enter "0" into the "Revision" box and this will remove all commits, but the "wiki" and "issues" areas will be preserved. Then - push the local repo back to Bitbucket again.&lt;/p&gt;

&lt;p&gt;There's an important catch though - because the above procedure affects history and re-assigns new "ids" to all your changesets, your teammates have to reclone the repo from the central repo again.&lt;/p&gt;

</description>
      <category>mercurial</category>
      <category>hg</category>
      <category>versioncontrol</category>
    </item>
  </channel>
</rss>
