CINXE.COM

<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width, initial-scale=1" data-next-head=""/><title data-next-head="">Blog • Oleksii Holub</title><link rel="icon" href="/favicon.png" data-next-head=""/><link rel="manifest" href="/manifest.json" data-next-head=""/><meta name="application-name" content="Oleksii Holub" data-next-head=""/><meta name="build-id" content="f2941d3" data-next-head=""/><meta name="description" content="Oleksii Holub (@tyrrrz) is a software developer, open-source maintainer, tech blogger and conference speaker" data-next-head=""/><meta name="keywords" content="" data-next-head=""/><meta name="theme-color" content="#a855f7" data-next-head=""/><meta property="og:type" content="website" data-next-head=""/><meta property="og:site_name" content="Oleksii Holub" data-next-head=""/><meta property="og:title" content="Blog • Oleksii Holub" data-next-head=""/><meta property="og:description" content="Oleksii Holub (@tyrrrz) is a software developer, open-source maintainer, tech blogger and conference speaker" data-next-head=""/><meta property="og:image" content="https://tyrrrz.me/logo.png" data-next-head=""/><meta name="twitter:title" content="Blog • Oleksii Holub" data-next-head=""/><meta name="twitter:site" content="@Tyrrrz" data-next-head=""/><meta name="twitter:creator" content="@Tyrrrz" data-next-head=""/><meta name="twitter:card" content="summary" data-next-head=""/><link rel="alternate" type="application/rss+xml" title="RSS Feed" href="https://tyrrrz.me/blog/rss.xml" data-next-head=""/><link rel="preload" href="/_next/static/css/1e0f3c26d8204452.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1e0f3c26d8204452.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-dbb64579bb13138d.js" defer=""></script><script src="/_next/static/chunks/framework-f1968f95cccbbbac.js" defer=""></script><script src="/_next/static/chunks/main-94208fdcf3f8f690.js" defer=""></script><script src="/_next/static/chunks/pages/_app-d4e04d7baaa8935d.js" defer=""></script><script src="/_next/static/chunks/pages/blog-fe57bb96f4919a31.js" defer=""></script><script src="/_next/static/NNsWp8pnlJAB1_jcoei3C/_buildManifest.js" defer=""></script><script src="/_next/static/NNsWp8pnlJAB1_jcoei3C/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="light"><div class="flex flex-col min-h-screen dark:bg-neutral-900 dark:text-neutral-200"><div class="h-1" style="width:0%;transition-property:width;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms"></div><div class="container max-w-4xl mx-auto"><header><div class="flex p-4 border-b-2 border-neutral-100 dark:border-neutral-800 items-center justify-between"><div class="text-xl font-mono font-semibold tracking-wide"><a class="" rel="noreferrer" href="/"><span class="text-neutral-400">://</span><span>tyrrrz.me</span></a></div><nav class="hidden sm:flex px-2 gap-x-2 text-lg"><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/">home</a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/projects">projects</a></div><div class="px-2 py-1 border-2 border-purple-500 rounded bg-purple-100 dark:bg-purple-900 transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/blog">blog</a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/speaking">speaking</a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/donate">donate</a></div><div class="flex ml-2 mt-0.5 text-2xl"><button class="text-blue-500 dark:text-yellow-500 cursor-pointer"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></button></div></nav><div class="sm:hidden flex gap-x-5 text-2xl"><button class="text-blue-500 dark:text-yellow-500 cursor-pointer"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></button><button class="sm:hidden"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg></button></div></div><div class="sm:hidden overflow-hidden"><nav class="-mt-[100%] p-2 border-b-2 border-neutral-100 dark:border-neutral-800 space-y-1 text-lg transition-all duration-300"><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/"><div>home</div></a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/projects"><div>projects</div></a></div><div class="px-2 py-1 border-2 border-purple-500 rounded bg-purple-100 dark:bg-purple-900 transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/blog"><div>blog</div></a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/speaking"><div>speaking</div></a></div><div class="px-2 py-1 border-2 border-transparent rounded transition-colors duration-300"><a class="hover:text-blue-500 dark:hover:text-blue-300" rel="noreferrer" href="/donate"><div>donate</div></a></div></nav></div></header><main class="mx-4 mt-6 mb-20"><div><div style="transition:opacity 400ms, transform 400ms;transform:translateY(20px);opacity:0"><section><h1 class="group my-4 text-3xl font-semibold"><span class="">Blog</span></h1><p class="my-4">I write about software design, architecture, programming languages, and other technical topics. Follow me on <a class="text-blue-500 dark:text-blue-300 hover:underline" href="https://twitter.com/Tyrrrz" target="_blank" rel="noreferrer">Twitter</a> or<!-- --> <a class="text-blue-500 dark:text-blue-300 hover:underline" href="https://bsky.app/profile/tyrrrz.me" target="_blank" rel="noreferrer">Bluesky</a>, or subscribe to the<!-- --> <a class="text-blue-500 dark:text-blue-300 hover:underline" href="/blog/rss.xml" target="_blank" rel="noreferrer">RSS Feed</a> <!-- -->to get notified when I post a new article.</p></section><section class="mt-8 space-y-6"><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2023</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/reverse-engineering-youtube-revisited">Reverse-Engineering YouTube: Revisited</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>February 4, 2023</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>20<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2021</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/monadic-comprehension-via-linq">Monadic Comprehension Syntax via LINQ in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>June 1, 2021</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>16<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2020</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/fluent-generics">Fluent Generics in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>November 17, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>9<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/fakes-over-mocks">Prefer Fakes Over Mocks</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>October 13, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>15<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/pure-impure-segregation-principle">Pure-Impure Segregation Principle</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>August 24, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>18<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/unit-testing-is-overrated">Unit Testing is Overrated</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>July 7, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>35<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/target-type-inference">Simulating Target-Type Inference in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>March 10, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>11<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/expression-trees">Working with Expression Trees in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>February 17, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>36<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/parsing-with-fparsec">Parsing in F# with FParsec</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>January 4, 2020</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>21<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2019</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/monadic-parser-combinators">Monadic Parser Combinators in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>November 10, 2019</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>14<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/creative-use-of-extension-methods">Creative Use of Extension Methods in C#</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>January 26, 2019</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>5<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2017</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/reverse-engineering-youtube">Reverse-Engineering YouTube</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>December 15, 2017</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>14<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/hotkey-editor-control-in-wpf">Hotkey Editor Control in WPF</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>February 2, 2017</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>3<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/wndproc-in-wpf">Custom WndProc Handlers in WPF</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>February 2, 2017</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>3<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2016</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/wpf-listbox-selecteditems-twoway-binding">WPF ListBox SelectedItems TwoWay Binding</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>November 1, 2016</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>5<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/wpf-treeview-selecteditem-twoway-binding">WPF TreeView SelectedItem TwoWay Binding</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>October 30, 2016</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>5<!-- --> min read</div></div></div></div></li><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/conditional-content-presenting-via-wpf-contentpresenter">Conditional Content Presenting via WPF ContentPresenter</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>October 28, 2016</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>2<!-- --> min read</div></div></div></div></li></ul></div></section><section><h2 class="group my-4 text-2xl font-semibold"><span class="">2015</span></h2><div class="ml-4"><ul class="border-l-[2px] border-purple-300 dark:border-purple-700 space-y-2"><li class="relative"><div class="absolute w-[8px] h-[8px] -left-[5px] top-2 md:top-3 rounded-full bg-purple-500"></div><div class="ml-4"><div class="text-lg"><a class="text-blue-500 dark:text-blue-300 hover:underline" rel="noreferrer" href="/blog/parsing-steam-market">Parsing Steam Market</a></div><div class="flex flex-wrap gap-x-3 font-light"><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg><div>September 26, 2015</div></div><div class="inline-flex items-center gap-1"><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg><div>4<!-- --> min read</div></div></div></div></li></ul></div></section></section></div></div></main></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"posts":[{"id":"reverse-engineering-youtube-revisited","title":"Reverse-Engineering YouTube: Revisited","date":"2023-02-04","readingTimeMins":19.786363636363635,"coverUrl":"/blog/reverse-engineering-youtube-revisited/cover.png","excerpt":"Back in 2017 I wrote an article in which I attempted to explain how YouTube works under the hood, how it serves streams to the client, and also how you can exploit that knowledge to download videos from the site. The primary goal of that write-up was to…"},{"id":"monadic-comprehension-via-linq","title":"Monadic Comprehension Syntax via LINQ in C#","date":"2021-06-01","readingTimeMins":16.1,"coverUrl":"/blog/monadic-comprehension-via-linq/cover.png","excerpt":"If you ask a C# developer to list the reasons why they enjoy working with the language, they will most likely put LINQ somewhere at the top. LINQ is an extremely convenient set of language tools that provide ways to query and transform data sequences of…"},{"id":"fluent-generics","title":"Fluent Generics in C#","date":"2020-11-17","readingTimeMins":9.127272727272727,"coverUrl":"/blog/fluent-generics/cover.png","excerpt":"Generic programming is a powerful feature available in many statically typed languages. It offers a way to write code that seamlessly operates against many different types, by targeting the features they share rather than the types themselves. This…"},{"id":"fakes-over-mocks","title":"Prefer Fakes Over Mocks","date":"2020-10-13","readingTimeMins":14.918181818181818,"coverUrl":"/blog/fakes-over-mocks/cover.png","excerpt":"The primary purpose of software testing is to detect any potential defects in a program before it reaches its intended consumers. This is typically achieved by establishing functional requirements which define supported user interactions as well as…"},{"id":"pure-impure-segregation-principle","title":"Pure-Impure Segregation Principle","date":"2020-08-24","readingTimeMins":17.545454545454547,"coverUrl":"/blog/pure-impure-segregation-principle/cover.png","excerpt":"Two months ago I published an article detailing why I think that Unit Testing is Overrated, which seemed to resonate quite a lot with readers, prompting very involved and interesting discussions. And although most commenters mainly shared their personal…"},{"id":"unit-testing-is-overrated","title":"Unit Testing is Overrated","date":"2020-07-07","readingTimeMins":35.263636363636365,"coverUrl":"/blog/unit-testing-is-overrated/cover.png","excerpt":"The importance of testing in modern software development is really hard to overstate. Delivering a successful product is not something you do once and forget about, but is rather a continuous and recurring process. With every line of code that changes,…"},{"id":"target-type-inference","title":"Simulating Target-Type Inference in C#","date":"2020-03-10","readingTimeMins":10.813636363636364,"coverUrl":"/blog/target-type-inference/cover.png","excerpt":"Above everything else in software development, I really enjoy building frameworks that enable other developers to create something cool. Sometimes, when chasing that perfect design I have in mind, I find myself coming up with weird hacks that push the C#…"},{"id":"expression-trees","title":"Working with Expression Trees in C#","date":"2020-02-17","readingTimeMins":35.75,"coverUrl":"/blog/expression-trees/cover.png","excerpt":"Expression trees is an obscure, although very interesting feature in .NET. Most people probably think of it as something synonymous with object-relational mapping frameworks, but despite being its most common use case, it's not the only one. There are a…"},{"id":"parsing-with-fparsec","title":"Parsing in F# with FParsec","date":"2020-01-04","readingTimeMins":21.445454545454545,"coverUrl":"/blog/parsing-with-fparsec/cover.png","excerpt":"Two months ago I wrote an article about Monadic Parser Combinators in C# and how you can use this approach to express complex recursive grammar. To extend this topic a bit, I also wanted to show how the same approach can be applied in a functional-first…"},{"id":"monadic-parser-combinators","title":"Monadic Parser Combinators in C#","date":"2019-11-10","readingTimeMins":13.822727272727272,"coverUrl":"/blog/monadic-parser-combinators/cover.png","excerpt":"A while ago I had to implement proper markdown parsing in DiscordChatExporter so that I could replace the brittle regular expressions I had been using. I had no idea how to approach this problem, so I spent days researching into this, eventually learning…"},{"id":"creative-use-of-extension-methods","title":"Creative Use of Extension Methods in C#","date":"2019-01-26","readingTimeMins":5.490909090909091,"coverUrl":"/blog/creative-use-of-extension-methods/cover.png","excerpt":"I'm sure that everyone with at least some background in C# is aware of extension methods — a nice feature that lets developers extend existing types with new methods.\n\nThis is extremely convenient in case you want to add functionality to types that you…"},{"id":"reverse-engineering-youtube","title":"Reverse-Engineering YouTube","date":"2017-12-15","readingTimeMins":14.118181818181819,"coverUrl":"/blog/reverse-engineering-youtube/cover.png","excerpt":"Almost a year ago, I started developing YoutubeExplode, a library that scrapes information on YouTube videos and lets you download them. Originally, my main motivation for developing it was simply to gain experience, as the task involved a lot of research…"},{"id":"hotkey-editor-control-in-wpf","title":"Hotkey Editor Control in WPF","date":"2017-02-02","readingTimeMins":3.3954545454545455,"excerpt":"GUI applications sometimes offer a way for users to configure hotkeys, typically using a textbox that records a combination of keys pressed inside it. Such control doesn't come out of the box in WPF, but we can implement it ourselves.\n\nHotkey class\n\nWPF…"},{"id":"wndproc-in-wpf","title":"Custom WndProc Handlers in WPF","date":"2017-02-02","readingTimeMins":2.659090909090909,"excerpt":"WndProc is a callback function that takes care of system messages sent from the operating system. Unlike WinForms, in WPF, it's not directly exposed to you as it's hidden beneath the framework's layer of abstraction.\n\nThere are times, however, when you…"},{"id":"wpf-listbox-selecteditems-twoway-binding","title":"WPF ListBox SelectedItems TwoWay Binding","date":"2016-11-01","readingTimeMins":4.668181818181818,"excerpt":"For some unclear reasons, WPF's ListBox control does not allow two-way binding on the SelectedItems property the way it does with SelectedItem. This could have been very useful when using multi-select to bind the whole list of selected items to the view…"},{"id":"wpf-treeview-selecteditem-twoway-binding","title":"WPF TreeView SelectedItem TwoWay Binding","date":"2016-10-30","readingTimeMins":4.740909090909091,"excerpt":"The longer I work with WPF, the more I notice how many things it's missing. Recently I realized that TreeView.SelectedItem property is read-only and unbindable. I think there's no point explaining why binding SelectedItem would be useful, so there should…"},{"id":"conditional-content-presenting-via-wpf-contentpresenter","title":"Conditional Content Presenting via WPF ContentPresenter","date":"2016-10-28","readingTimeMins":2.059090909090909,"excerpt":"Sometimes you may find yourself facing a problem of having to switch between multiple presentations based on some condition. For example, you may want to display the same list of items differently depending on whether multi-select is enabled or not.\n\nThe…"},{"id":"parsing-steam-market","title":"Parsing Steam Market","date":"2015-09-26","readingTimeMins":3.5136363636363637,"excerpt":"At some point, I was contracted to make a WordPress plugin that displays some basic information on an item from Steam Market. It had to show its name, image and the current lowest price.\n\nI was initially hoping that there would be some sort of API for…"}]},"__N_SSG":true},"page":"/blog","query":{},"buildId":"NNsWp8pnlJAB1_jcoei3C","isFallback":false,"gsp":true,"scriptLoader":[]}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10