<?xml version="1.0" encoding="UTF-8"?>
<feed version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"
				   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
				   xmlns:dc="http://purl.org/dc/elements/1.1/"
				   xmlns:atom="http://www.w3.org/2005/Atom"
				   xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
				   xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
	<channel>
		<title>Anssi Piirainen</title>
		<link>https://anssi.dev/</link>
		<description>Personal website of Anssi Piirainen</description>
		<copyright>Copyright 2026 Anssi Piirainen</copyright>
		<language>en-EN</language>
		<author>
			<name>Anssi Piirainen</name>
			<email>api@iki.fi</email>
		</author>
		
		<item>
			<title>Are you a craftsman or an engineer, or both?</title>
			
			<link>https://anssi.dev/post/are-you-a-craftsman-or-an-engineer-or-both.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>What are the different software crafsmanship and engineering practises and when are they imnportant in the different life stages of a development organization</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative w-400 h-auto mx-auto&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/inSCjogFWJ-300.avif 300w, https://anssi.dev/img/inSCjogFWJ-600.avif 600w, https://anssi.dev/img/inSCjogFWJ-900.avif 900w, https://anssi.dev/img/inSCjogFWJ-4592.avif 4592w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/inSCjogFWJ-300.webp 300w, https://anssi.dev/img/inSCjogFWJ-600.webp 600w, https://anssi.dev/img/inSCjogFWJ-900.webp 900w, https://anssi.dev/img/inSCjogFWJ-4592.webp 4592w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/inSCjogFWJ-300.png 300w, https://anssi.dev/img/inSCjogFWJ-600.png 600w, https://anssi.dev/img/inSCjogFWJ-900.png 900w, https://anssi.dev/img/inSCjogFWJ-4592.png 4592w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Woodworking&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/inSCjogFWJ-300.jpeg&quot; width=&quot;4592&quot; height=&quot;3056&quot; srcset=&quot;https://anssi.dev/img/inSCjogFWJ-300.jpeg 300w, https://anssi.dev/img/inSCjogFWJ-600.jpeg 600w, https://anssi.dev/img/inSCjogFWJ-900.jpeg 900w, https://anssi.dev/img/inSCjogFWJ-4592.jpeg 4592w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p class=&quot;photo-credit&quot;&gt;
Photo by &lt;a href=&quot;https://unsplash.com/@jbonunsplash?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash&quot;&gt;Jean-Baptiste D.&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/a-man-working-on-a-piece-of-wood-44Vp65XxXs4?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Recently I&#39;ve been thinking a lot about the software development process—things like automation, testing, and scaling. I&#39;ve also been thinking about code quality. The following questions have been on my mind: What does good-quality code look like? How long should files and individual functions be? Should you break it into smaller chunks, and how should those chunks look? I&#39;ve also been thinking about the timing— &lt;em&gt;when&lt;/em&gt; these things matter the most. What are the most important things when the development organization is young (like a startup), and how do they change as the company grows (like a scale-up company)?&lt;/p&gt;
&lt;p&gt;When thinking about different approaches to software development, the concepts of Software Craftsmanship and Software Engineering came to mind. I think the first time I became familiar with the craftsmanship idea in software was when I read the classic book &lt;em&gt;The Pragmatic Programmer&lt;/em&gt; by Andrew Hunt and David Thomas, back when it was published in 1999. The book introduced the idea of software craftsmanship, emphasizing the importance of care and pride in software development. I highly recommend it. Good stuff in it.&lt;/p&gt;
&lt;p&gt;I started thinking—Is it enough to master craftsmanship skills? Or should you also master engineering skills? What does it even mean to be an engineer, and is that different from being a craftsman? Let&#39;s first define what these terms mean.&lt;/p&gt;
&lt;h2&gt;Craftsmanship&lt;/h2&gt;
&lt;p&gt;Software Craftsmanship is a &lt;em&gt;mindset and movement&lt;/em&gt; that emphasizes the art, skill, and pride in software development.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Quality over Quantity&lt;/em&gt;: Focuses on writing clean, maintainable, and well-crafted code.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Continuous Improvement&lt;/em&gt;: Encourages lifelong learning and mastering the craft through practice.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Professionalism&lt;/em&gt;: Values responsibility, ethics, and delivering value to customers.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Mentorship &amp;amp; Apprenticeship&lt;/em&gt;: Emphasizes teaching and learning through experience, much like in traditional guilds.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Developer Autonomy&lt;/em&gt;: Encourages individual developers to take ownership and pride in their work.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Craft over Process&lt;/em&gt;: Prefers strong technical skills and judgment rather than strict reliance on formalized processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think all of the things, except for Developer Autonomy perhaps, in this list are good things and should be practiced by every developer. Let&#39;s talk about Developer Autonomy later and why that might not be so important might even be a bad thing in a team setting.&lt;/p&gt;
&lt;h2&gt;Engineering&lt;/h2&gt;
&lt;p&gt;Software Engineering is a &lt;em&gt;discipline&lt;/em&gt; that applies engineering principles to software development.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Formal Processes &amp;amp; Methodologies: Emphasizes standardized methods like Agile, Waterfall, DevOps, etc.&lt;/li&gt;
&lt;li&gt;Planning and Documentation: Strong focus on requirements, design, testing, documentation, and lifecycle management.&lt;/li&gt;
&lt;li&gt;Scalability and Reliability: Ensures systems are engineered to scale and handle complexity reliably.&lt;/li&gt;
&lt;li&gt;Team Coordination: Encourages defined roles, responsibilities, and structured communication.&lt;/li&gt;
&lt;li&gt;Measurement &amp;amp; Control: Uses metrics, models, and reviews to ensure quality and predictability.&lt;/li&gt;
&lt;li&gt;Compliance and Risk Management: Addresses regulatory, safety, and compliance concerns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this also sounds good to me. But are they really necessary and worth the effort? When should you use them? Let&#39;s dive into that next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A good read on this topic&lt;/strong&gt; is &lt;em&gt;Modern Software Engineering: Doing What Works to Build Better Software Faster&lt;/em&gt;, by David Farley.&lt;/p&gt;
&lt;h2&gt;When to do what&lt;/h2&gt;
&lt;h3&gt;Startup&lt;/h3&gt;
&lt;p&gt;A young startup is first looking for product-market fit. They need to be agile and flexible to adapt to changing market conditions. In this kind of organization, you don&#39;t want anything to slow you down—this rules out most of the engineering practices. &lt;strong&gt;I would rule out the following&lt;/strong&gt;, just because these are unnecessary burdens that make you slower when you need to be agile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;Extensive documentation&lt;/li&gt;
&lt;li&gt;Investing for scalability (premature optimization)&lt;/li&gt;
&lt;li&gt;Compliance and risk management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Automated testing is most likely a waste in this phase. When you pivot once a month, you’ll throw away your codebase. The code that you build for the MVPs should be minimal and probably shouldnot include many tests. But what if you can use AI to generate the tests? That might change your mind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I would use most of the craftmanship practises in a startup.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;After finding PMF (product-market fit)&lt;/h2&gt;
&lt;p&gt;Then when (and if) you finally find product-market fit, you need to start adapting your approach. You need to prepare yourself to take over the world. Perhaps you&#39;ll reach millions in MRR within the next few months. Your traffic and number of users grow, and your development team grows in size. All of these require you to change your way of doing things.&lt;/p&gt;
&lt;p&gt;When you grow, the engineering practices become more important. You need to invest in scalability and reliability, and you need to document your code and processes. You also need to plan your development and coordinate your team. You need to make sure that you can onboard new developers effectively—and that means the spaghetti code you developed in a rush when the company was young and small might need to be rewritten or heavily refactored.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;About timing: It can be tricky to know when to change from startup-style cowboy coding to a more disciplined approach. This is because the shift the company experiences is rarely an event that you can recognize. This means that you need to start shifting your style gradually, starting with small changes and gradually increasing the level of discipline.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Another reading tip&lt;/strong&gt;: &lt;em&gt;Tidy First?&lt;/em&gt; by Kent Beck. This book introduces &amp;quot;tidyings&amp;quot; that are small refactorings that can be done quickly and easily. These are small improvements that you can do every time you need to add a new feature to the codebase. Applying tidyings continuesly slowly moves your code to a better structure. I find the to be a great approach and avoids the risks associated with big refactorings.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Craftsmanship remains important&lt;/em&gt;. Developer Autonomy, a craftmanship practice, will lose relevance as the company grows. Teams become more important than individuals. Shared code ownership, as emphasized in XP and other agile methodologies, becomes more important. It&#39;s the team that should have autonomy and ownership of the codebase that is the team&#39;s responsibility.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, are you a craftsman, an engineer, or both? I think the answer is: you should probably aim to be both, but at different times and in different ways. Early on, when you&#39;re in the fast-paced world of startups, craftsmanship helps you move fast and keep things just good enough without overthinking. But as your product matures and your team grows, engineering becomes more and more essential. You need structure. You need repeatability. You need to build things that won’t fall apart the moment you scale.&lt;/p&gt;
&lt;p&gt;It’s not a binary choice. It’s a spectrum—and knowing when to lean more into craftsmanship and when to bring in the engineering discipline is part of being a great developer. So maybe the real goal is to become someone who can adapt. Someone who knows how to build quick and dirty when speed matters, but who also knows how to slow down, clean up, and build things that last when the time is right.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Working with Web Components</title>
			
			<link>https://anssi.dev/post/working-with-web-components.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>My experience switching from React to Web Components for the Spot Canvas project.</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative w-64 h-auto mx-auto&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/8rG8jUuD8t-300.avif 300w, https://anssi.dev/img/8rG8jUuD8t-600.avif 600w, https://anssi.dev/img/8rG8jUuD8t-900.avif 900w, https://anssi.dev/img/8rG8jUuD8t-2939.avif 2939w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/8rG8jUuD8t-300.webp 300w, https://anssi.dev/img/8rG8jUuD8t-600.webp 600w, https://anssi.dev/img/8rG8jUuD8t-900.webp 900w, https://anssi.dev/img/8rG8jUuD8t-2939.webp 2939w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/8rG8jUuD8t-300.png 300w, https://anssi.dev/img/8rG8jUuD8t-600.png 600w, https://anssi.dev/img/8rG8jUuD8t-900.png 900w, https://anssi.dev/img/8rG8jUuD8t-2939.png 2939w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Spot Canvas logo&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/8rG8jUuD8t-300.jpeg&quot; width=&quot;2939&quot; height=&quot;2965&quot; srcset=&quot;https://anssi.dev/img/8rG8jUuD8t-300.jpeg 300w, https://anssi.dev/img/8rG8jUuD8t-600.jpeg 600w, https://anssi.dev/img/8rG8jUuD8t-900.jpeg 900w, https://anssi.dev/img/8rG8jUuD8t-2939.jpeg 2939w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;I&#39;ve been working on an alternative to TradingView since late last year, and it has been a great experience. I&#39;ve been using TradingView to follow crypto prices for several years now. At some point, I got the idea that it might be worth implementing my own alternative, so I started experimenting with it.&lt;/p&gt;
&lt;p&gt;It&#39;s now been almost six months of working on this on and off during evenings and weekends, and it&#39;s come pretty far already. I even have a website for it: &lt;a href=&quot;https://www.spotcanvas.com/&quot;&gt;Spot Canvas&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Why Web Components?&lt;/h2&gt;
&lt;p&gt;I&#39;ve been working with React for close to eight years and wanted to try something different. Probably two years ago, I would have chosen React for this project as well, but since then, I&#39;ve grown a bit bored with it. I&#39;ve also seen how React apps can become big and hard to maintain over time — I&#39;m no longer convinced that it&#39;s the best option for web applications.&lt;/p&gt;
&lt;p&gt;So these are the main reasons I chose to go with web components: I wanted to try something different and also learn something new. In hobby projects, it&#39;s good to experiment with different technologies — that&#39;s how you learn and grow.&lt;/p&gt;
&lt;h2&gt;How Has It Been?&lt;/h2&gt;
&lt;p&gt;I&#39;ve enjoyed working with web components! 🙂 It&#39;s been like a breath of fresh air. It has reminded me of my time working with Flowplayer when I was building the first version using Flash — I can use similar patterns and design choices that I had with the object-oriented structure I built for Flowplayer. I&#39;m less of an OOP fanatic nowadays, so I&#39;m avoiding deep class hierarchies and things like that, but I am indeed using classes. I think classes can be a good fit for user interfaces.&lt;/p&gt;
&lt;p&gt;State management has been nice to work with. None of that &lt;code&gt;useState&lt;/code&gt; or context-based weirdness I was used to in React. I&#39;m using the ES Proxy-based solution from &lt;a href=&quot;https://github.com/tonioloewald/xinjs&quot;&gt;xinjs&lt;/a&gt;, and it works great. It makes things streamlined and convenient.&lt;/p&gt;
&lt;p&gt;You can find a standalone demo of the chart &lt;a href=&quot;https://www.spotcanvas.com/demo/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Tech Stack&lt;/h2&gt;
&lt;p&gt;The tech stack I&#39;m using for the frontend (the chart app) looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Web components using the &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Plain old CSS (embedded in TypeScript files, as is typical with Lit)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bun.sh/&quot;&gt;Bun&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firestore for live subscriptions for real-time price and indicator updates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The backend runs on Google Cloud with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Bun&lt;/li&gt;
&lt;li&gt;Firestore&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Roadmap&lt;/h2&gt;
&lt;p&gt;So far, I&#39;ve implemented the basic price chart with trading pair selection, time granularity selection, and candlestick rendering. You can pan and zoom in both the timeline and the price axis. At first, it felt easy to develop, and I was making good progress, but as more features started piling up, things naturally became more complex — no surprises there.&lt;/p&gt;
&lt;p&gt;I just released an Indicators feature, which is essential for this kind of product. You can read the release announcement &lt;a href=&quot;https://www.spotcanvas.com/blog/indicators/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Getting to this point has taken six months.&lt;/p&gt;
&lt;p&gt;The road ahead includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technical analysis tools (drawing tools)&lt;/li&gt;
&lt;li&gt;User accounts with settings, etc.&lt;/li&gt;
&lt;li&gt;The ability to save chart layouts&lt;/li&gt;
&lt;li&gt;An asset library for saving favorite trading pairs&lt;/li&gt;
&lt;li&gt;Scripting for custom indicators and for interacting with the chart&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Want to Help?&lt;/h2&gt;
&lt;p&gt;Let me know if you&#39;re interested in collaborating on this project. I&#39;m convinced that there&#39;s a need for a solid trading chart alternative, and with the help of vibe coding, we can build something great! 🙂 I could use help on both the development and marketing sides. You can reach me at anssip@gmail.com.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>The most effective way to learn a programming language</title>
			
			<link>https://anssi.dev/post/the-most-effective-way-to-learn-a-programming-language.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>How I used my hobby project as a learning ground for learning the Rust programming language</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative 400&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/Qp4Jk1nlwY-300.avif 300w, https://anssi.dev/img/Qp4Jk1nlwY-600.avif 600w, https://anssi.dev/img/Qp4Jk1nlwY-900.avif 900w, https://anssi.dev/img/Qp4Jk1nlwY-1024.avif 1024w&quot; sizes=&quot;400&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/Qp4Jk1nlwY-300.webp 300w, https://anssi.dev/img/Qp4Jk1nlwY-600.webp 600w, https://anssi.dev/img/Qp4Jk1nlwY-900.webp 900w, https://anssi.dev/img/Qp4Jk1nlwY-1024.webp 1024w&quot; sizes=&quot;400&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/Qp4Jk1nlwY-300.png 300w, https://anssi.dev/img/Qp4Jk1nlwY-600.png 600w, https://anssi.dev/img/Qp4Jk1nlwY-900.png 900w, https://anssi.dev/img/Qp4Jk1nlwY-1024.png 1024w&quot; sizes=&quot;400&quot; /&gt;&lt;img alt=&quot;Eagle&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/Qp4Jk1nlwY-300.jpeg&quot; width=&quot;1024&quot; height=&quot;1024&quot; srcset=&quot;https://anssi.dev/img/Qp4Jk1nlwY-300.jpeg 300w, https://anssi.dev/img/Qp4Jk1nlwY-600.jpeg 600w, https://anssi.dev/img/Qp4Jk1nlwY-900.jpeg 900w, https://anssi.dev/img/Qp4Jk1nlwY-1024.jpeg 1024w&quot; sizes=&quot;400&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;I decided to learn the Rust programming language, and here&#39;s how I did it.&lt;/p&gt;
&lt;p&gt;I started by reading a book about Rust. It&#39;s a good way to begin, but it&#39;s not enough. You don&#39;t really learn programming just by reading a book; you have to write code. So, I began writing a simple password manager in Rust. I&#39;m not exactly sure how I came up with this project idea, but I think it was because I had been using several paid apps like BitWarden and Dashlane, and I wanted to see if I could create something to replace them.&lt;/p&gt;
&lt;p&gt;My setup for the project was pretty basic: I had read a bit of the book, but my knowledge was very minimal. Still, I managed to get started with the project and implement some basic features through a lot of trial and error. I had to look up a lot of things on the internet and read the Rust documentation. I wrestled a lot with the syntax, particularly with the Rust borrow checker. The borrow checker and Rust&#39;s memory safety features make Rust challenging to learn. However, what really surprised me was that when I finally got the program to compile, it actually worked and did what I wanted it to do. There was usually no need for debugging and troubleshooting—it just worked. This got me very excited about Rust. It&#39;s a strict and tough language to learn, but once you get your code to compile, it&#39;s very likely to work as expected.&lt;/p&gt;
&lt;p&gt;With my very basic understanding of Rust, I was able to implement a simple password manager that worked. I called it &lt;a href=&quot;https://github.com/anssip/passlane&quot;&gt;Passlane, and I made it available in Github&lt;/a&gt;. It had all the features I needed: I could add, edit, and delete passwords. I could search for passwords in the vault and use them. I could also generate new passwords. I was actually using the program as a replacement for Dashlane.&lt;/p&gt;
&lt;p&gt;At this point, I stopped learning Rust for a few months and moved on to other hobby projects that interested me more at the time.&lt;/p&gt;
&lt;h2&gt;Coming back to Rust again&lt;/h2&gt;
&lt;p&gt;After a year, I decided to return to Rust and continue learning it. This time, I bought some physical books about Rust from Amazon. Armed with these books, I resumed reading and learning, and I also started working on the password manager project again. I began heavily refactoring the messy code I had written earlier. As I learned more about Rust, I replaced huge parts of the codebase. I delved into traits, lifetimes, error handling, and many other concepts. With each new piece of knowledge, I revisited the password manager codebase.&lt;/p&gt;
&lt;p&gt;When I learned about async programming, I realized that for my CLI-based project, I didn&#39;t really need async. So, I removed all async code and the Tokio runtime, which significantly simplified the code.&lt;/p&gt;
&lt;p&gt;When I studied error handling and the ? operator, I replaced all the unwrap() calls with ?. I embraced the Result type, implemented my own Error type, and used it throughout the codebase. This made the codebase simpler—and simpler is usually better.&lt;/p&gt;
&lt;p&gt;I also studied the Rust module system and refactored the codebase to use modules and much smaller files. This was another improvement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This process of studying and then immediately applying my new knowledge to the fairly large codebase of my project was the core of my learning process. This approach really solidified my understanding. In my opinion, it&#39;s the most effective way to learn a programming language.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can see my extensive rewriting and refactoring in the &lt;a href=&quot;https://github.com/anssip/passlane/commits/master/&quot;&gt;commit history&lt;/a&gt; of the project. I also significantly changed the functionality by switching to Keepass as the storage backend in version 2.3.0 of the app. This change was made to ensure users could trust that I was taking the right steps to keep their data safe. Additionally, I added support for TOTP codes (Timed One-Time Passwords) for 2-factor authentication.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;My conclusion is this: When learning programming, you should write code as much as possible. At the same time, read books and documentation. Use AI and chatbots to ask questions. Asking for help from tools like Copilot and ChatGPT can be very effective ways to learn, but that&#39;s a topic for another blog post.&lt;/p&gt;
&lt;h2&gt;The project&lt;/h2&gt;
&lt;p&gt;I have my password manager &lt;a href=&quot;https://github.com/anssip/passlane&quot;&gt;available in Github&lt;/a&gt;. It&#39;s called Passlane, and at this point, I&#39;m quite proud of it. Check it out and let me know what you think!&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Open Source Sports Betting</title>
			
			<link>https://anssi.dev/post/open-source-sports-betting.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>My new project; a sports betting website</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/bwx2RgqqO7-300.avif 300w, https://anssi.dev/img/bwx2RgqqO7-600.avif 600w, https://anssi.dev/img/bwx2RgqqO7-900.avif 900w, https://anssi.dev/img/bwx2RgqqO7-1740.avif 1740w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/bwx2RgqqO7-300.webp 300w, https://anssi.dev/img/bwx2RgqqO7-600.webp 600w, https://anssi.dev/img/bwx2RgqqO7-900.webp 900w, https://anssi.dev/img/bwx2RgqqO7-1740.webp 1740w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/bwx2RgqqO7-300.png 300w, https://anssi.dev/img/bwx2RgqqO7-600.png 600w, https://anssi.dev/img/bwx2RgqqO7-900.png 900w, https://anssi.dev/img/bwx2RgqqO7-1740.png 1740w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Soccer game&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/bwx2RgqqO7-300.jpeg&quot; width=&quot;1740&quot; height=&quot;1160&quot; srcset=&quot;https://anssi.dev/img/bwx2RgqqO7-300.jpeg 300w, https://anssi.dev/img/bwx2RgqqO7-600.jpeg 600w, https://anssi.dev/img/bwx2RgqqO7-900.jpeg 900w, https://anssi.dev/img/bwx2RgqqO7-1740.jpeg 1740w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p class=&quot;photo-credit&quot;&gt;
Photo by Abigail Keenan on &lt;a href=&quot;https://unsplash.com/photos/group-of-people-playing-soccer-on-soccer-field-8-s5QuUBtyM&quot;&gt;Unsplash&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Over the past few months, I have been deeply engrossed in a new personal project: the development of a sports betting
website. The genesis of this endeavor was a conversation with former colleagues who are currently engaged in the iGaming
industry. This discussion was particularly nostalgic for me, as I previously worked with a company that designed a
sports betting product for the Finnish gaming operator PAF.&lt;/p&gt;
&lt;p&gt;My objective was to explore the implementation of this concept utilizing contemporary web technologies, specifically
GraphQL, the latest Next.js framework with server components, and server actions. My particular interest lay in
harnessing the capabilities of GraphQL subscriptions to seamlessly provide real-time odds updates to end-users during
live (in-play) sporting events.&lt;/p&gt;
&lt;p&gt;To provide an overview, the backend infrastructure is powered by the Kotlin programming language. This backend hosts a
robust GraphQL API, supported by a relational database. The Kotlin application is hosted on AWS within Elastic Container
Service (ECS), while the database resides within AWS RDS. The Continuous Integration (CI) workflow is effectively
managed through GitHub actions.&lt;/p&gt;
&lt;p&gt;On the frontend, we have a modern Next.js application that capitalizes on server components and server actions. Vercel
serves as the hosting platform for the frontend. I used Auth0 for authentication and authorization.&lt;/p&gt;
&lt;p&gt;The event fixtures and odds are fetched from the Odds API.&lt;/p&gt;
&lt;p&gt;The feature set currently includes the following functionalities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Real-time updates on live events, complete with scores.&lt;/li&gt;
&lt;li&gt;Listings of upcoming events.&lt;/li&gt;
&lt;li&gt;Betting on live events and upcoming events&lt;/li&gt;
&lt;li&gt;Tracking and management of bets on the dedicated bets page.&lt;/li&gt;
&lt;li&gt;Payouts from winning bets are added to the user&#39;s gaming account balance.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can explore the live website at https://www.parabolicbet.com/.&lt;/p&gt;
&lt;p&gt;Additionally, for anyone interested, my code repositories are readily available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/anssip/ultrabet&quot;&gt;Backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/anssip/ultrabet-ui&quot;&gt;Frontend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m enthusiastic about this project and aspire to see it thrive in the real world. If you happen to represent
a gaming operator with aspirations for a collaborative venture, please do not hesitate to reach out. I am
available for freelance opportunities to bring this project to fruition. Together, we can make it a resounding success! 🚀&lt;/p&gt;
&lt;p&gt;In any case I see this as a great learning experience and a way to perhaps disrupt the iGaming industry a bit. I don&#39;t
think this industry has seen too much open source projects so far 😊&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>To sprint or not to sprint?</title>
			
			<link>https://anssi.dev/post/to-sprint-or-not-to-sprint.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Scrum and Sprinting helps when you need focus and maximum performance</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/rPW4UoV66s-300.avif 300w, https://anssi.dev/img/rPW4UoV66s-600.avif 600w, https://anssi.dev/img/rPW4UoV66s-900.avif 900w, https://anssi.dev/img/rPW4UoV66s-3442.avif 3442w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/rPW4UoV66s-300.webp 300w, https://anssi.dev/img/rPW4UoV66s-600.webp 600w, https://anssi.dev/img/rPW4UoV66s-900.webp 900w, https://anssi.dev/img/rPW4UoV66s-3442.webp 3442w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/rPW4UoV66s-300.png 300w, https://anssi.dev/img/rPW4UoV66s-600.png 600w, https://anssi.dev/img/rPW4UoV66s-900.png 900w, https://anssi.dev/img/rPW4UoV66s-3442.png 3442w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Sprinters on track&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/rPW4UoV66s-300.jpeg&quot; width=&quot;3442&quot; height=&quot;2295&quot; srcset=&quot;https://anssi.dev/img/rPW4UoV66s-300.jpeg 300w, https://anssi.dev/img/rPW4UoV66s-600.jpeg 600w, https://anssi.dev/img/rPW4UoV66s-900.jpeg 900w, https://anssi.dev/img/rPW4UoV66s-3442.jpeg 3442w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;Here is a lesson I learned recently: Scrum sprints are excellent if you want to squeeze the maximum performance from your team. Scrum also helps to gain sharp focus on the most important things that you should finish first.&lt;/p&gt;
&lt;h2&gt;Kanban vs Scrum&lt;/h2&gt;
&lt;p&gt;Our team started with a Kanban process. We had a backlog of tasks, and we started working with daily standup meetings and biweekly retrospectives. In the beginning, things were looking good, and we started finishing work slowly but surely. Then things began to go downhill.&lt;/p&gt;
&lt;p&gt;There had been a recent major release of the system we were assigned to work on. The release was made before our new team inherited the project and the codebase. The release was a bit disastrous, and bugs started pouring in, making our backlog bigger and bigger. As the issues kept piling up, our focus started shifting from one critical problem to another, hurting our ability to concentrate and fix these issues. In a sudden move, our team decided to opt-in to a &lt;strong&gt;micromanagement approach&lt;/strong&gt; for solving these problems. Our boss started assigning every task to individual developers! A lot of focus was put into prioritizing the backlog items, which helped a lot.
The best idea I can think of for a situation like this is to use Good Old Scrum Sprints.&lt;/p&gt;
&lt;p&gt;Needless to say, I was not too happy with the micromanagement approach and started thinking about what would be the best process to use in our situation. The best idea I have come up with is to use &lt;strong&gt;Good Old Scrum Sprints&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do some sprint planning: Prioritize the issues, and take the most important ones to your first sprint backlog.&lt;/li&gt;
&lt;li&gt;Make sure that the team commits to completing the tasks in the backlog.&lt;/li&gt;
&lt;li&gt;Release at the end of each sprint.&lt;/li&gt;
&lt;li&gt;The team should self-organize and decide how to accomplish the sprint goal. No need for micromanagement during the sprint.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The duration of the sprints could be one or perhaps two weeks.&lt;/p&gt;
&lt;p&gt;This has the following benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will have a focused sprint backlog containing all the items that the team needs to finish.&lt;/li&gt;
&lt;li&gt;Your team can concentrate on the work included in the backlog and ignore everything else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the sprint is complete, the team continues with the next one repeating the cycle again.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;I&#39;m now thinking that Scrum works better than Kanban for a new team. It is also a better choice when you have time pressure to finish things urgently.&lt;/p&gt;
&lt;p&gt;However, I think pushing your team to its absolute maximum performance cannot be sustained in the long run: people burn out, and you risk losing them. Kanban is an excellent choice if you have a stable product and things are going smoothly.&lt;/p&gt;
&lt;p&gt;Here is a great &lt;a href=&quot;https://resources.scrumalliance.org/Article/scrum-vs-kanban&quot;&gt;comparison of Scrum vs Kanban from the Scrum Alliance&lt;/a&gt;.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>I&#39;m Joining Emmy Clothing Company</title>
			
			<link>https://anssi.dev/post/i&amp;#39;m-joining-emmy-clothing-company.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>My motives for a new career move</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative 200&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/ctb5MH_pIT-300.avif 300w, https://anssi.dev/img/ctb5MH_pIT-500.avif 500w&quot; sizes=&quot;200&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/ctb5MH_pIT-300.webp 300w, https://anssi.dev/img/ctb5MH_pIT-500.webp 500w&quot; sizes=&quot;200&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/ctb5MH_pIT-300.png 300w, https://anssi.dev/img/ctb5MH_pIT-500.png 500w&quot; sizes=&quot;200&quot; /&gt;&lt;img alt=&quot;Emmy logo&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/ctb5MH_pIT-300.jpeg&quot; width=&quot;500&quot; height=&quot;157&quot; srcset=&quot;https://anssi.dev/img/ctb5MH_pIT-300.jpeg 300w, https://anssi.dev/img/ctb5MH_pIT-500.jpeg 500w&quot; sizes=&quot;200&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;After four years of working as a freelance developer and a consultant, I joined a circular economy startup called Emmy.&lt;/p&gt;
&lt;p&gt;I have concentrated entirely on hands-on programming and tech work during the past four years. I wanted to have this tech focus to help me level up my development game which had gotten a bit rusty during my 10-year stint as an &lt;a href=&quot;https://anssipiirainen.com/post/leavingflow/&quot;&gt;entrepreneur in Flowplayer&lt;/a&gt;. In my opinion, this worked out pretty well to the extent that pure tech focus is starting to feel a bit &lt;em&gt;boring&lt;/em&gt;, and it&#39;s time for me to take the next step in my path.&lt;/p&gt;
&lt;h2&gt;About Emmy&lt;/h2&gt;
&lt;p&gt;To give some context to this blog post, let me first briefly introduce Emmy. The following quote is from &lt;a href=&quot;https://www.linkedin.com/company/emmy-clothing-company/&quot;&gt;Emmy&#39;s LinkedIn page&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Emmy is a pioneering service for consumers to recirculate brand apparel that they no longer need. Reinventing the way people sell and buy second-hand items, we offer an effortless turnkey service for people to wake up the money sleeping in their wardrobes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;strong&gt;circular fashion economy&lt;/strong&gt; is growing faster than the traditional linear fashion market. &lt;a href=&quot;https://www.therobinreport.com/the-circular-fashion-future/&quot;&gt;According to this article&lt;/a&gt;, the resale segment grows 24x faster than retail. With 20 %+ monthly sales growth, Emmy is one of the fastest-growing apparel recirculation operators in the Nordics and Europe.&lt;/p&gt;
&lt;h2&gt;Motives and Purpose of Work&lt;/h2&gt;
&lt;p&gt;There are a couple of motives that pushed me to this move. First, I want to expand my role beyond purely technical hands-on work. In Emmy, we plan to grow the development efforts and expand our development team with both in-house and external developers. Being one of the company&#39;s first in-house tech people means that I get to drive the successful growth of these efforts.&lt;/p&gt;
&lt;p&gt;Building software is more than just programming: As an engineer, I think of it as &lt;strong&gt;Software Engineering&lt;/strong&gt;, which takes a holistic look at building software. It takes more than just typing on the keyboard to build quality software effectively: People and the team, communication, processes, company culture, learning and knowledge building and similar &amp;quot;soft issues&amp;quot; all play important roles here.&lt;/p&gt;
&lt;p&gt;Suppose I would continue my career concentrating mainly on programming as a consultant. In that case, I think I would be wasting 70% of the skills and experience that I have gained during my 30 years career in software engineering. I&#39;m looking to Emmy now as an opportunity to widen my professional scope so that I can use my current skills fully and also learn more.&lt;/p&gt;
&lt;p&gt;Now more than ever, the &lt;strong&gt;purpose&lt;/strong&gt; of work has become vital to me. I hope to experience the excitement of working in a startup environment again. I look forward to being an &lt;strong&gt;integral part of making Emmy a success&lt;/strong&gt;. Being part of something great provides purpose for me. As a consultant, I have missed that.&lt;/p&gt;
&lt;p&gt;Emmy&#39;s role in the circular economy is the second part of the meaningful purpose. Following surely sounds like a cliché, but Emmy is making the world a better place by providing means for people to recycle.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We need to recycle to save the planet. I want to advance the transition to a circular economy where recycling is the norm. This mission feels more purposeful than the ones I have been working on lately: ad-tech and consulting to bill an hourly rate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The tech stack&lt;/h2&gt;
&lt;p&gt;Saving the world is nice, but it&#39;s even better with cool tech. I will be working on state-of-the-art technology in Emmy, including tools like Shopify and GraphQL APIs, serverless &amp;amp; Lambda, Next.js, Contentful.&lt;/p&gt;
&lt;p&gt;I&#39;m excited to dive into these technologies during the upcoming weeks and months. Especially the serverless stuff I look forward to learning and mastering.&lt;/p&gt;
&lt;p&gt;Stay tuned for another blog post, perhaps more about the tech aspects of building an e-commerce store. Watch this space for the next update :-)&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Review Environments are Awesome</title>
			
			<link>https://anssi.dev/post/review-environments-are-awesome.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>My motives for a new career move</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/d3OVjkQCvd-300.avif 300w, https://anssi.dev/img/d3OVjkQCvd-600.avif 600w, https://anssi.dev/img/d3OVjkQCvd-900.avif 900w, https://anssi.dev/img/d3OVjkQCvd-3072.avif 3072w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/d3OVjkQCvd-300.webp 300w, https://anssi.dev/img/d3OVjkQCvd-600.webp 600w, https://anssi.dev/img/d3OVjkQCvd-900.webp 900w, https://anssi.dev/img/d3OVjkQCvd-3072.webp 3072w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/d3OVjkQCvd-300.png 300w, https://anssi.dev/img/d3OVjkQCvd-600.png 600w, https://anssi.dev/img/d3OVjkQCvd-900.png 900w, https://anssi.dev/img/d3OVjkQCvd-3072.png 3072w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Traffic buttons&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/d3OVjkQCvd-300.jpeg&quot; width=&quot;3072&quot; height=&quot;2048&quot; srcset=&quot;https://anssi.dev/img/d3OVjkQCvd-300.jpeg 300w, https://anssi.dev/img/d3OVjkQCvd-600.jpeg 600w, https://anssi.dev/img/d3OVjkQCvd-900.jpeg 900w, https://anssi.dev/img/d3OVjkQCvd-3072.jpeg 3072w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;h2&gt;What is a review environment?&lt;/h2&gt;
&lt;p&gt;Let&#39;s start with some background information and describe the &amp;quot;traditional&amp;quot; practice that I don&#39;t recommend anymore. The traditional development model looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have two &lt;strong&gt;branches&lt;/strong&gt; in version control: a development branch and the main branch.&lt;/li&gt;
&lt;li&gt;Have two different &lt;strong&gt;environments&lt;/strong&gt;: one for development and one for production.&lt;/li&gt;
&lt;li&gt;All development work goes first to the development branch.&lt;/li&gt;
&lt;li&gt;Development branch gets deployed to the development environment.&lt;/li&gt;
&lt;li&gt;Testing happens in the development environment.&lt;/li&gt;
&lt;li&gt;Deployment to production occasionally happens (at random intervals) when some set of features is ready and tested in the development environment.&lt;/li&gt;
&lt;li&gt;The production deployment is triggered by a marge of the development branch to the main branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, there is a new kid in town, in which everything happens around pull requests. It changes the game (for the better). The new model relies on temporary review environments that exist for the lifetime of each PR.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The team splits the work into small tasks, each of which will be deployed to production once implemented.&lt;/li&gt;
&lt;li&gt;When a developer starts working on a task, he creates a new branch forking it out from main.&lt;/li&gt;
&lt;li&gt;When a developer completes coding a task, she creates a new &lt;strong&gt;pull request&lt;/strong&gt; (PR) towards main.&lt;/li&gt;
&lt;li&gt;The new PR triggers the CI engine to build a new review environment and deploy the PR codebase into this new environment.&lt;/li&gt;
&lt;li&gt;Another developer reviews and tests the change in the review environment.&lt;/li&gt;
&lt;li&gt;Once the reviewer accepts the PR, the author of the PR merges it, which then triggers the CI to build &amp;amp; deploy the task&#39;s changes to production.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/FN-cDtwICq-300.avif 300w, https://anssi.dev/img/FN-cDtwICq-600.avif 600w, https://anssi.dev/img/FN-cDtwICq-900.avif 900w, https://anssi.dev/img/FN-cDtwICq-1730.avif 1730w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/FN-cDtwICq-300.webp 300w, https://anssi.dev/img/FN-cDtwICq-600.webp 600w, https://anssi.dev/img/FN-cDtwICq-900.webp 900w, https://anssi.dev/img/FN-cDtwICq-1730.webp 1730w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/FN-cDtwICq-300.png 300w, https://anssi.dev/img/FN-cDtwICq-600.png 600w, https://anssi.dev/img/FN-cDtwICq-900.png 900w, https://anssi.dev/img/FN-cDtwICq-1730.png 1730w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;PR in Github&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/FN-cDtwICq-300.jpeg&quot; width=&quot;1730&quot; height=&quot;1008&quot; srcset=&quot;https://anssi.dev/img/FN-cDtwICq-300.jpeg 300w, https://anssi.dev/img/FN-cDtwICq-600.jpeg 600w, https://anssi.dev/img/FN-cDtwICq-900.jpeg 900w, https://anssi.dev/img/FN-cDtwICq-1730.jpeg 1730w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;This screenshot shows a pull request in Github. There is a View deployment button that opens the app in the PR specific temporary review environment. Reviewers need to test the app in this environment before the PR author is allowed to merge it. Github allows for blocking the merge until a predefined number of reviewers have accepted the change.&lt;/p&gt;
&lt;h2&gt;Workflow&lt;/h2&gt;
&lt;p&gt;Review builds enable continuous and seamless delivery to production. The workflow allows the team to release in small steps incrementally. There is no need for any orchestration of testing in dedicated development and testing environments, nor is there any coordination of production releases.&lt;/p&gt;
&lt;p&gt;All development work needs to be split into small tasks that can be individually released. Each task/PR should result in a change that can be pushed to production without making the product inconsistent or broken. The tasks should be small so that they can be reviewed and tested with reasonable effort. Ideally, each task should be coded in one or two days and then reviewed, fixed &amp;amp; refined in another one or two days. Having larger chunks as tasks is not recommended because massive changes are harder to manage and review and are more prone to break the production system. The goal is to have a continuous stream of tiny releases that happen multiple times per day.&lt;/p&gt;
&lt;p&gt;Implementing a large change-set, a so-called epic, requires completing several small tasks and PRs. Each of these PRs modifies the product in some small way, and because it usually does not make sense to reveal half-baked features, we need to delay the moment when we make these modifications visible to the end-users. One way to achieve this delayed visibility control is to build a &lt;strong&gt;feature gate&lt;/strong&gt; into the product.&lt;/p&gt;
&lt;p&gt;The feature gate works like this: When features are behind the feature gate, they are only visible to testers, developers and other trusted users. When we open the gate, everything that was hidden behind it become visible to all. The moment to lift the feature gate usually comes when all the PRs related to the epic become ready, and the whole epic becomes complete.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There you have it: A continuous stream of production releases: It all just kind of automatically happens when developers work on the tasks pushing them forward using pull requests.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Reviews&lt;/h2&gt;
&lt;p&gt;Pull request reviews are at the centre of the workflow. They act as a quality guard to ensure that only inspected code changes flow into the product.&lt;/p&gt;
&lt;p&gt;The review consists of two main parts: the code review and testing of the functionality.&lt;/p&gt;
&lt;p&gt;All testing happens as part of the PR review. Some organisations might have QA persons (testers) as part of the development teams, but many teams and organisations can do very well without them. Developers can be as equipped to do the reviews as any professional tester would be.&lt;/p&gt;
&lt;p&gt;Note that also automated testing can happen as part of the PR builds. The best practice is to run a comprehensive unit test suite at this point to ensure that the deployment only occurs when the automated tests have passed.&lt;/p&gt;
&lt;h2&gt;How to implement automatic review deployments?&lt;/h2&gt;
&lt;p&gt;Many cloud hosting platforms like Heroku and Vercel offer PR based review builds and deployments out-of-the-box. Heroku calls them review apps and Vercel calls them preview environments.
It is also reasonably straightforward to implement them using Github Actions. I recently enabled them for one React app using &lt;a href=&quot;https://aws.amazon.com/cdk/&quot;&gt;AWS CDK&lt;/a&gt; to deploy the app to S3 and Cloudfront. Julien Goux does a good job documenting this approach in &lt;a href=&quot;https://dev.to/jgoux/preview-environments-per-pull-request-using-aws-cdk-and-github-actions-bfi&quot;&gt;this dev.to article&lt;/a&gt;.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>How to use country-specific keyboards with Linux</title>
			
			<link>https://anssi.dev/post/how-to-use-country-specific-keyboards-with-linux.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>How I configured Linux to behave with a Finnish keyboard layout.</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative 450&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/TkMkzmQHFH-300.avif 300w, https://anssi.dev/img/TkMkzmQHFH-600.avif 600w, https://anssi.dev/img/TkMkzmQHFH-640.avif 640w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/TkMkzmQHFH-300.webp 300w, https://anssi.dev/img/TkMkzmQHFH-600.webp 600w, https://anssi.dev/img/TkMkzmQHFH-640.webp 640w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/TkMkzmQHFH-300.png 300w, https://anssi.dev/img/TkMkzmQHFH-600.png 600w, https://anssi.dev/img/TkMkzmQHFH-640.png 640w&quot; sizes=&quot;700&quot; /&gt;&lt;img alt=&quot;Knowledge and keyboards mean power&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/TkMkzmQHFH-300.jpeg&quot; width=&quot;640&quot; height=&quot;428&quot; srcset=&quot;https://anssi.dev/img/TkMkzmQHFH-300.jpeg 300w, https://anssi.dev/img/TkMkzmQHFH-600.jpeg 600w, https://anssi.dev/img/TkMkzmQHFH-640.jpeg 640w&quot; sizes=&quot;700&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;I recently switched to using Linux in my desktop computer that I use in my daily work. Getting the keyboard to work correctly was probably the thing that took me the longest time to get right.&lt;/p&gt;
&lt;p&gt;I am using an Apple keyboard with a Finnish key layout. I am using a Macbook as my mobile computer, and therefore I wanted to have a similar keyboard layout in this desktop computer. Switching between the two computers is more comfortable if things are not too different between these two computers that I use. Typing comes mostly from muscle memory, and typing speed suffers dramatically if inconsistency of keyboard layouts starts to cause muscle memory to fail when typing.&lt;/p&gt;
&lt;p&gt;Let&#39;s get to the dirty details on how I got things going.&lt;/p&gt;
&lt;h2&gt;KDE Settings&lt;/h2&gt;
&lt;p&gt;I am using KDE as my desktop package, and I have set it to use &amp;quot;Apple | Apple Aluminium (ANSI)&amp;quot; as the keyboard model in System Settings. I make the keyboard layout to be &amp;quot;Finnish (classic)&amp;quot;.&lt;/p&gt;
&lt;p&gt;One would expect this to be enough, and everything should &amp;quot;just work&amp;quot;s with the plain old Apple computers. But nope, with these settings I was not able to type square brackets, curly brackets, pipes, tildes etc. that one uses a lot when programming or working on the command line.&lt;/p&gt;
&lt;p&gt;I got these &amp;quot;special characters&amp;quot; working using a tool called Autokey.&lt;/p&gt;
&lt;h2&gt;Autokey&lt;/h2&gt;
&lt;p&gt;I am using &lt;a href=&quot;https://github.com/autokey/autokey&quot;&gt;Autokey&lt;/a&gt; for getting the special characters in their familiar places. Following shows all the Autokey scripts that I have in place:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# at.py to send the &#39;@&#39; character&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+2&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;@&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# backslach &#39;&#92;&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+&amp;lt;shift&gt;+7&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;&#92;&#92;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# left curly bracket &#39;{&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+&amp;lt;shift&gt;+8&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;{&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# right curly bracket &#39;}&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+&amp;lt;shift&gt;+9&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# dollar sign &#39;$&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+4&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;&amp;lt;shift&gt;+4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# pipe &#39;|&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+7&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;|&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# left square bracket &#39;[&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+8&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;[&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# right square bracket &#39;]&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+9&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;backspace&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# tilde &#39;~&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Hotkey: &amp;lt;alt&gt;+]&lt;/span&gt;
keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;~&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scripts shown above will make my Apple keyboard with a Finnish key layout work like expected. There could be still some missing mappings that should be there, but I have not discovered them yet. I can easily add more mappings if I find out additional needs.&lt;/p&gt;
&lt;p&gt;The mappings you will need for some other country-specific or regional layout will be different. The approach that I describe here will work for other layouts as well.&lt;/p&gt;
&lt;p&gt;I am using &lt;code&gt;autokey-qt&lt;/code&gt; which is a GUI tool that I use to enter the scripts I listed above. There is also a GTK version called &lt;code&gt;autokey-gtk&lt;/code&gt; available for Gnome and other GTK based environments. One script is capable of handling only one character, and therefore you will need to have a separate script for each of them. You can record a hotkey shortcut for each of the scripts. In the screenshot below, you can see my &lt;code&gt;at.py&lt;/code&gt; script, which I have mapped to hotkey &lt;code&gt;&amp;lt;alt&amp;gt;+2&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;image-container overflow-hidden relative 450&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/xyfMvVYgFN-300.avif 300w, https://anssi.dev/img/xyfMvVYgFN-600.avif 600w, https://anssi.dev/img/xyfMvVYgFN-900.avif 900w, https://anssi.dev/img/xyfMvVYgFN-938.avif 938w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/xyfMvVYgFN-300.webp 300w, https://anssi.dev/img/xyfMvVYgFN-600.webp 600w, https://anssi.dev/img/xyfMvVYgFN-900.webp 900w, https://anssi.dev/img/xyfMvVYgFN-938.webp 938w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/xyfMvVYgFN-300.png 300w, https://anssi.dev/img/xyfMvVYgFN-600.png 600w, https://anssi.dev/img/xyfMvVYgFN-900.png 900w, https://anssi.dev/img/xyfMvVYgFN-938.png 938w&quot; sizes=&quot;700&quot; /&gt;&lt;img alt=&quot;Knowledge is power&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/xyfMvVYgFN-300.jpeg&quot; width=&quot;938&quot; height=&quot;614&quot; srcset=&quot;https://anssi.dev/img/xyfMvVYgFN-300.jpeg 300w, https://anssi.dev/img/xyfMvVYgFN-600.jpeg 600w, https://anssi.dev/img/xyfMvVYgFN-900.jpeg 900w, https://anssi.dev/img/xyfMvVYgFN-938.jpeg 938w&quot; sizes=&quot;700&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the script sends two different keys in sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;a backspace to wipe out the character that my keyboard normally sends when I press ALT + 2.&lt;/li&gt;
&lt;li&gt;the character I want to Autokey to emit when in press ALT + 2: the @ symbol.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Make Autokey start when KDE starts&lt;/h2&gt;
&lt;p&gt;To finish the setup, we need to have Autokey starting automatically. To do so, I went to System Preferences / Autostart, and added &lt;code&gt;/usr/local/bin/autokey-qt&lt;/code&gt; as a program that gets started automatically when the KDE desktop starts.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>When and why to use TypeScript</title>
			
			<link>https://anssi.dev/post/when-and-why-to-use-typescript.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Often it makes sense to use TypeScript instead of vanilla JavaScript</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative 450&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/UtbBpWh9ch-300.avif 300w, https://anssi.dev/img/UtbBpWh9ch-600.avif 600w, https://anssi.dev/img/UtbBpWh9ch-900.avif 900w, https://anssi.dev/img/UtbBpWh9ch-1318.avif 1318w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/UtbBpWh9ch-300.webp 300w, https://anssi.dev/img/UtbBpWh9ch-600.webp 600w, https://anssi.dev/img/UtbBpWh9ch-900.webp 900w, https://anssi.dev/img/UtbBpWh9ch-1318.webp 1318w&quot; sizes=&quot;700&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/UtbBpWh9ch-300.png 300w, https://anssi.dev/img/UtbBpWh9ch-600.png 600w, https://anssi.dev/img/UtbBpWh9ch-900.png 900w, https://anssi.dev/img/UtbBpWh9ch-1318.png 1318w&quot; sizes=&quot;700&quot; /&gt;&lt;img alt=&quot;Knowledge is power&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/UtbBpWh9ch-300.jpeg&quot; width=&quot;1318&quot; height=&quot;796&quot; srcset=&quot;https://anssi.dev/img/UtbBpWh9ch-300.jpeg 300w, https://anssi.dev/img/UtbBpWh9ch-600.jpeg 600w, https://anssi.dev/img/UtbBpWh9ch-900.jpeg 900w, https://anssi.dev/img/UtbBpWh9ch-1318.jpeg 1318w&quot; sizes=&quot;700&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;I have programmed exclusively with JavaScript for the past five years. I enjoy programming in JavaScript, and until recently, I saw no reason why I would switch away from it. I did not understand why I would compromise the speed and agility that JavaScript brings.&lt;/p&gt;
&lt;p&gt;I love the dynamic nature of JS. It allows me to quickly implement new solutions. My thinking was that TypeScript&#39;s added type declarations and type checking would make me slower and less productive.&lt;/p&gt;
&lt;p&gt;Recently my opinion has changed, and in my latest projects, I have begun to understand the benefits of TypeScript.&lt;/p&gt;
&lt;p&gt;In this article, I explain the reasons why I changed my mind about TypeScript and why I think that many projects will benefit from using it instead of vanilla JavaScript.&lt;/p&gt;
&lt;h2&gt;Working solo&lt;/h2&gt;
&lt;p&gt;I think that JavaScript is adequate when working alone, but it&#39;s sub-optimal for teams.&lt;/p&gt;
&lt;p&gt;JavaScript is suitable for quick development. When working solo, It does not matter &lt;em&gt;that much&lt;/em&gt; if the code is hard to understand for others. The chances are that you, as the sole author of the code base, can understand it well enough to maintain and enhance it.&lt;/p&gt;
&lt;p&gt;JavaScript allows for rapid development partly because it does not require any types or interfaces to be written. You are not &amp;quot;wasting&amp;quot; any time in writing this typing.&lt;/p&gt;
&lt;p&gt;In the past, I have implemented sizeable codebases using vanilla JavaScript and I have maintained them for years. This has worked well because I have been the only person working on these projects.&lt;/p&gt;
&lt;h2&gt;Teamwork&lt;/h2&gt;
&lt;p&gt;I recently started working on a huge JavaScript codebase that had been authored by one developer over several years. My task was to implement a small enhancement to it but was unable to do it because it was simply too hard for me to understand what was going on in the code.&lt;/p&gt;
&lt;p&gt;The original developer obviously was able to still make changes to it, but for me, the code did not make much sense. The coding style, or the lack of it, was making my brain hurt.&lt;/p&gt;
&lt;p&gt;We decided to start migrating the project to use TypeScript. We identified two areas that would benefit the most from type annotations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interfaces for all message types. The project is using a messaging system, and several different kinds of messages are sent and received between modules and functions.&lt;/li&gt;
&lt;li&gt;Interfaces for all DB types. Just like the message objects, these DB records are also passed between modules and functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We added these interfaces and proceeded to add typing to all functions that have these objects in their signatures as parameters or return values. &lt;strong&gt;It&#39;s these type declarations that make the code easier to understand.&lt;/strong&gt; You can immediately see from the type declarations what data the function takes in. Interfaces describe the structure and typing of all complex parameters. There is no need to inspect the calling code, or the function implementation, or the unit tests, to figure out what fields these complex objects are composed of.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A shared understanding of the code becomes &lt;em&gt;vital&lt;/em&gt; when you are working as a team. Added type information brings added understanding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Fewer bugs&lt;/h2&gt;
&lt;p&gt;The type information is also consumed and checked by the TypeScript compiler. The compiler will tell you when your code is not type safe.&lt;/p&gt;
&lt;p&gt;It&#39;s the classic benefit that type checking brings: &lt;em&gt;Catching bugs earlier in the development process&lt;/em&gt;. You will catch and fix bugs during development instead of your customers finding them when your app is in production.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/typescript/comments/c079bt/airbnb_think_38_of_their_bugs_could_have_been/&quot;&gt;Airbnb reported&lt;/a&gt; a &lt;strong&gt;38% reduction in bugs&lt;/strong&gt; by adding TypeScript to their development process.&lt;/p&gt;
&lt;p&gt;The TypeScript compiler has a &lt;em&gt;strict option&lt;/em&gt; which enables more stringent type checking. With the strict option enabled, it will flag out more potential problems.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If using TypeScript is better than using plain JavaScript because of the compiler warnings, why wouldn’t you want to turn the compiler warnings dial up to ten?”
‐ &lt;a href=&quot;https://www.stevefenton.co.uk/2018/01/embracing-typescript-strict-mode/&quot;&gt;Embracing the TypeScript Strict Mode – Steve Fenton&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&#39;s probably best to not use the strict mode when migrating an existing JS projects to use TypeScript. In our TypeScript migration project, we have started with the strict checks disabled, which makes it possible for us to have the build passing and the program running with less effort. If we decide to enable the strict checks, we have more work to be done before the compiler is happy enough to produce a runnable program.&lt;/p&gt;
&lt;h2&gt;The cost of using TypeScript&lt;/h2&gt;
&lt;p&gt;TypeScript does not come for free. There is some cost associated with using it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It takes effort to write type annotations and to maintain them&lt;/li&gt;
&lt;li&gt;Type annotations add clutter to the code base&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should be noted that our industry is using several techniques to catch bugs. Typescript and its static type checking is only one of these techniques. Here are some of the recommended practises: Test Driven Development, pair programming, code reviews, design reviews, and linting.&lt;/p&gt;
&lt;p&gt;If you are using TDD and code reviews, you are likely already efficient in recovering bugs early. In this case, adding TypeScript will not have such a significant impact on improving quality. You should consider your situation and try to determine whether the benefits of TypeScript will out-weight the costs.&lt;/p&gt;
&lt;h2&gt;Object-Oriented vs Functional Programming&lt;/h2&gt;
&lt;p&gt;During the past two years, I have been learning to use functional programming in my JS projects. I was a bit worried that TypeScript would not be a good fit for functional programming. My uninformed thinking was that TypeScript would make my code similar to Java, where everything is based on classes, and everything has to be an Object.&lt;/p&gt;
&lt;p&gt;It turns out that my worries were unjustified. TypeScript builds on top of JavaScript and everything that I can do with JavaScript I can also do in TypeScript. I am not forced to use classes, or to declare types for all variables. Typing is optional and can be added where most appropriate.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;When to use TypeScript?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You will benefit from TypeScript when some of the following are true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have a rich data model where data objects are passed between modules and functions.&lt;/li&gt;
&lt;li&gt;You have more than one developer working in the project.&lt;/li&gt;
&lt;li&gt;You are developing an open-source library. It needs TypeScript typings so that people can use with their TS code bases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why use TypeScript?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Type checking catches bugs early.&lt;/li&gt;
&lt;li&gt;It enables teamwork by increasing the understandability and maintainability of the code. Because of this same reason, it makes it also faster to bring new team members up to speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When is it better to stick with JavaScript?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are some situations where I would not use TypeScript but would go with vanilla JavaScript instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have experienced JavaScript developers who are using TDD and code reviews&lt;/li&gt;
&lt;li&gt;Quick prototypes are quick to develop with JavaScript&lt;/li&gt;
&lt;li&gt;Single-use scripts (migration scripts etc.) are an excellent fit to be done with JavaScript&lt;/li&gt;
&lt;/ul&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>How to add object fields conditionally</title>
			
			<link>https://anssi.dev/post/how-to-add-object-fields-conditionally.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>A neat syntax for adding fields to an object conditionally without polluting it with undefined fields.</description>
			<content type="html">&lt;p&gt;I often run into situations where I need to construct an object in JavaScript, and I want to set fields to this object conditionally. That could be an options object that I am passing as a parameter to some other function or API. I have only some of the field values available, and only those should be part of the parameter object.&lt;/p&gt;
&lt;p&gt;To describe the situation, let&#39;s take a look at an example with a straightforward syntax using if statements.&lt;/p&gt;
&lt;div class=&quot;image-container overflow-hidden relative 450&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/WveNY9uBMG-300.avif 300w, https://anssi.dev/img/WveNY9uBMG-600.avif 600w, https://anssi.dev/img/WveNY9uBMG-900.avif 900w, https://anssi.dev/img/WveNY9uBMG-1160.avif 1160w&quot; sizes=&quot;600&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/WveNY9uBMG-300.webp 300w, https://anssi.dev/img/WveNY9uBMG-600.webp 600w, https://anssi.dev/img/WveNY9uBMG-900.webp 900w, https://anssi.dev/img/WveNY9uBMG-1160.webp 1160w&quot; sizes=&quot;600&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/WveNY9uBMG-300.png 300w, https://anssi.dev/img/WveNY9uBMG-600.png 600w, https://anssi.dev/img/WveNY9uBMG-900.png 900w, https://anssi.dev/img/WveNY9uBMG-1160.png 1160w&quot; sizes=&quot;600&quot; /&gt;&lt;img alt=&quot;Mutation&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/WveNY9uBMG-300.jpeg&quot; width=&quot;1160&quot; height=&quot;696&quot; srcset=&quot;https://anssi.dev/img/WveNY9uBMG-300.jpeg 300w, https://anssi.dev/img/WveNY9uBMG-600.jpeg 600w, https://anssi.dev/img/WveNY9uBMG-900.jpeg 900w, https://anssi.dev/img/WveNY9uBMG-1160.jpeg 1160w&quot; sizes=&quot;600&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;Here I&#39;m mutating the object conditionally, setting fields for values that are available. I don&#39;t like this kind of mutations, because they make the code hard to follow.&lt;/p&gt;
&lt;p&gt;Here is another attempt:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value1 &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value2 &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &amp;quot;solution&amp;quot; pollutes the object with fields that have undefined values. These fields will be present in the object, even though their values are undefined. With this kind of an object, you cannot be sure what happens when you pass it to the external library you are building this object for: It might behave just fine, or you might get errors, or worst, it might work in some unpredictable ways.&lt;/p&gt;
&lt;h2&gt;The Way&lt;/h2&gt;
&lt;p&gt;Following is the cleanest syntax that I know of that solves these problems:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value1 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value2 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value2 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like this a lot: Looks clean, does not add unnecessary fields to the object, and it does not use ugly if statements.&lt;/p&gt;
&lt;p&gt;With the ES6 shorthand property syntax it looks even more clean:&lt;/p&gt;
&lt;div class=&quot;image-container overflow-hidden relative 450&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/wjfidgVGYR-300.avif 300w, https://anssi.dev/img/wjfidgVGYR-600.avif 600w, https://anssi.dev/img/wjfidgVGYR-858.avif 858w&quot; sizes=&quot;600&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/wjfidgVGYR-300.webp 300w, https://anssi.dev/img/wjfidgVGYR-600.webp 600w, https://anssi.dev/img/wjfidgVGYR-858.webp 858w&quot; sizes=&quot;600&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/wjfidgVGYR-300.png 300w, https://anssi.dev/img/wjfidgVGYR-600.png 600w, https://anssi.dev/img/wjfidgVGYR-858.png 858w&quot; sizes=&quot;600&quot; /&gt;&lt;img alt=&quot;screenshot&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/wjfidgVGYR-300.jpeg&quot; width=&quot;858&quot; height=&quot;624&quot; srcset=&quot;https://anssi.dev/img/wjfidgVGYR-300.jpeg 300w, https://anssi.dev/img/wjfidgVGYR-600.jpeg 600w, https://anssi.dev/img/wjfidgVGYR-858.jpeg 858w&quot; sizes=&quot;600&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;I found this trick when I was looking at some code that my ex co-developer had produced, and have used it twice since I discovered it. I hope you find it useful too.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>The new anssipiirainen.com</title>
			
			<link>https://anssi.dev/post/the-new-anssipiirainen.com.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Whipped up a new blog using Hugo.</description>
			<content type="html">&lt;p&gt;A week ago, I decided to set up a new website for this blog. Somehow I always have the urge to learn something new. Just programming with the same old tools and languages becomes boring, and something new is needed to spice up my life.&lt;/p&gt;
&lt;p&gt;This year I have been blogging a lot. Writing is an area that I want to keep on doing also in the future. Up until this point, my blog was in Squarespace. I felt that I wanted to improve my blogging workflow: It should be easier for me to work on, and the result should be better looking and friendlier for the reader. I also wanted to learn something new while building my new blogging &amp;quot;platform&amp;quot;.&lt;/p&gt;
&lt;p&gt;I ended up building this website using &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;. Hugo is a static site generator and provides a developer-friendly &amp;quot;hands-on&amp;quot; experience for the user. It&#39;s a totally different experience than the GUI centered approach that Squarespace offers. With Hugo, everything happens in a text editor and in the command-line interface.&lt;/p&gt;
&lt;p&gt;Hugo is implemented using the Go programming language. I am interested in learning Go, and one big reason for choosing Hugo was to get some exposure to Go.&lt;/p&gt;
&lt;h2&gt;Deployment&lt;/h2&gt;
&lt;p&gt;This blog is deployed in Amazon S3 using Hugo Deploy. It&#39;s using Cloudfront as the CDN.&lt;/p&gt;
&lt;p&gt;I used a nifty trick to enable a piece of URL rewriting using &lt;em&gt;Lambda@Edge&lt;/em&gt; to enable directory index documents. For example, the path of this page is &lt;code&gt;/post/new-blog-site/&lt;/code&gt; and this needs to be mapped to &lt;code&gt;/post/new-blog-site/index.html&lt;/code&gt; because the actual page &lt;code&gt;index.html&lt;/code&gt; file recides in that path in the S3 bucket. With a quick web search, I found out that this can be achieved using a &lt;a href=&quot;https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/&quot;&gt;Lambda script that is deployed and runs in all Cloudfront edge locations&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The result&lt;/h2&gt;
&lt;p&gt;As you can see from the looks of this site, this is quite bare-bones. It looks nerdy. Just like I wanted it to look like :smile:&lt;/p&gt;
&lt;p&gt;I have direct access to the CSS files, which is great, because I also want to improve my styling skills.&lt;/p&gt;
&lt;p&gt;The page templates are there available for hacking whenever I feel the need to work on the site layout or on additional features. I could integrate a mailing list to this site eventually, for example. The &lt;a href=&quot;https://golang.org/pkg/text/template/&quot;&gt;Go page templating system&lt;/a&gt; seems powerful.&lt;/p&gt;
&lt;p&gt;I can now write the blog posts without leaving my favorite text editor, Visual Studio Code. I write my blog posts in Markdown which is a format that Hugo directly understands. Previously I had to copy/paste all text to the post UI in Squarespace website, and now, with Hugo, my workflow is more straightforward: The markdown file sits in Hugo&#39;s content directory all the time when I work on it.&lt;/p&gt;
&lt;p&gt;I just found out that there is a Grammarly plugin for Visual Studio Code. I use Grammarly for checking the spelling grammar of my posts. The VSCode plugin seems a bit flaky at the moment, but it could be a great addition to my workflow.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>How to Organise Remote Work for High Productivity</title>
			
			<link>https://anssi.dev/post/how-to-organise-remote-work-for-high-productivity.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Organizing remote work in a software development team</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/FxQYqCNIWn-300.avif 300w, https://anssi.dev/img/FxQYqCNIWn-600.avif 600w, https://anssi.dev/img/FxQYqCNIWn-900.avif 900w, https://anssi.dev/img/FxQYqCNIWn-1500.avif 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/FxQYqCNIWn-300.webp 300w, https://anssi.dev/img/FxQYqCNIWn-600.webp 600w, https://anssi.dev/img/FxQYqCNIWn-900.webp 900w, https://anssi.dev/img/FxQYqCNIWn-1500.webp 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/FxQYqCNIWn-300.png 300w, https://anssi.dev/img/FxQYqCNIWn-600.png 600w, https://anssi.dev/img/FxQYqCNIWn-900.png 900w, https://anssi.dev/img/FxQYqCNIWn-1500.png 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;My iMac&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/FxQYqCNIWn-300.jpeg&quot; width=&quot;1500&quot; height=&quot;1000&quot; srcset=&quot;https://anssi.dev/img/FxQYqCNIWn-300.jpeg 300w, https://anssi.dev/img/FxQYqCNIWn-600.jpeg 600w, https://anssi.dev/img/FxQYqCNIWn-900.jpeg 900w, https://anssi.dev/img/FxQYqCNIWn-1500.jpeg 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;The popularity of remote work is on the rise, and in the past weeks, the demand has seen exponential growth. Following are some of the non-obvious reasons for using remote workers and remote teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Companies can look for the best talent all over the world. They can look outside their neighbourhood for new talent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Companies want to place people in markets that are in different parts of the world. It makes sense to have people close to the customers, and in the same time zones with them.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Right mow, the Coronavirus is forcing many companies to have their employees working from home. Suddenly there are a lot of employers and employees trying to figure out how to operate in this unusual situation.&lt;/p&gt;
&lt;p&gt;Companies are always looking to increase their productivity and profits. Based on my ten years of experience as a remote worker, I can say that not every remote team is working effectively and achieving high productivity. The world is full of companies and organisations that have no clue about remote work and are now suffering when the virus forces them to practise it.&lt;/p&gt;
&lt;p&gt;Companies need to have the right practises in place to make remote working effective.&lt;/p&gt;
&lt;h2&gt;The best practises for remote teams&lt;/h2&gt;
&lt;p&gt;There are a lot of practises that can improve remote working. In this article, I&#39;m presenting the top three.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backlog of work&lt;/li&gt;
&lt;li&gt;Asynchronous communication&lt;/li&gt;
&lt;li&gt;Timely feedback&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Backlog of work&lt;/h3&gt;
&lt;p&gt;The Backlog ensures that people have a place to go when they need new work.&lt;/p&gt;
&lt;p&gt;Many software development organisations know how to use backlogs and visual boards for organising work. The backlog is a prioritised list of work that the organisation wants to complete. The backlog needs to be available for access from anywhere and online tools, like Github boards and Trello, are excellent for this.&lt;/p&gt;
&lt;p&gt;These tools are not only for distributed teams: Many on-site teams are using them as well.  Visual boards are useful tools for everyone. For remote teams, having tools like this is more important because in a distributed setting, people cannot rely on face-to-face communication nearly as much as in a co-located setting. For remote teams, the practice of asking around does not work well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All items in the backlog should be planned.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;People need to be able to trust that the task they take from the backlog represents work that the organisation wants to complete. People don&#39;t want to invest their time on stuff that is not needed. Nobody likes wasting their time on busywork.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everyone in the team should be familiar with the tasks that are in the backlog.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Joint planning is one way to make sure that everyone is familiar with the tasks. The team could assemble the backlog as a team exercise. In Scrum, the whole team gets together periodically to plan the tasks for the next iteration of the project. After the sprint planning session,  the team members know the tasks well enough to be able to start working on them later in the iteration.&lt;/p&gt;
&lt;p&gt;I have seen the Scrum backlog planning approach work well for building a working backlog.&lt;/p&gt;
&lt;p&gt;Don&#39;t be too attached on the idea of the backlog here. The most important thing to take away from this is that &lt;em&gt;there needs to be some mechanism in place which ensures that people always know what to do next&lt;/em&gt;. You and your team can together decide what the best tool for you is.&lt;/p&gt;
&lt;h2&gt;Asynchronous communication&lt;/h2&gt;
&lt;p&gt;Programming requires deep concentration. You should not interrupt a programmer at work if you don&#39;t want to destroy his focus. Achieving the &lt;em&gt;flow state&lt;/em&gt; of tight focus takes time, and interruptions will harm focusing. In fact, frequent interruptions may prevent a person to never reach the flow state. His concentration never gets deep enough to achieve the state of &lt;em&gt;deep work&lt;/em&gt; and high productivity.&lt;/p&gt;
&lt;p&gt;In recent years, open offices have become less popular. Companies have realised that the constant communication and interaction which happens in open workspaces can kill productivity. Knowledge workers, like programmers, need private spaces to be able to concentrate.&lt;/p&gt;
&lt;p&gt;That being said, not all work is deep work that happens in the flow state. Knowledge workers need to communicate to share information and to synchronise their work to achieve shared goals. This is where async communication comes in.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Asynchronous communication happens when you send a message without expecting an immediate response. Take email, for example. I open and respond to the email several hours later.&lt;/li&gt;
&lt;li&gt;Synchronous communication happens when you send a message, and the recipient reads it and responds immediately. Meetings or water-cooler discussions are examples of synchronous communication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Async communication can improve productivity because people can concentrate on it when they have a break from their deep work sessions. People should be able to alternate between  deep work and between communication at their own rhythm — this would be the recipe for high productivity.&lt;/p&gt;
&lt;p&gt;Companies should encourage async communication. It should be part of the standard working culture of the company. Tools like Slack can be excellent for async communication. To make async communication effective in a tool like Slack, have the following rules in place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;people don&#39;t expect an immediate answer to their questions&lt;/li&gt;
&lt;li&gt;people don&#39;t feel the need to answer right away when someone asks a question from them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should also be noted that async communication can make it possible for people to work in different time zones. Not all messaging needs to happen when the time zones overlap. You can send a question when the recipient on the other time zone is sleeping, and then receive the answer the following morning when you wake up.&lt;/p&gt;
&lt;h2&gt;Timely feedback&lt;/h2&gt;
&lt;p&gt;Sometimes developers run into situations when they cannot make progress on a task before they get some from someone else. A decision needs to be made, a question needs to be answered, or some new tool needs to be purchased, before work can continue.&lt;/p&gt;
&lt;p&gt;You don&#39;t want your developer to be stuck for long times. You should quickly help him (or her) so that he is free to continue his work.&lt;/p&gt;
&lt;p&gt;Async communication means that you don&#39;t need to answer peoples messages and questions immediately. On the flip side of this, you should not take too long with your answers and feedback. Long delays can be frustrating and, for obvious reasons,  delays are disastrous for the overall productivity.&lt;/p&gt;
&lt;h2&gt;Summing up&lt;/h2&gt;
&lt;p&gt;To succeed with distributed teams, the top things to prioritise are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make sure you have a solid backlog of work available at all times&lt;/li&gt;
&lt;li&gt;Promote asynchronous communication&lt;/li&gt;
&lt;li&gt;Provide timely feedback&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks for reading and keep up the good (remote) work!&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Functional Programming tricks for simplifying and improving code</title>
			
			<link>https://anssi.dev/post/functional-programming-tricks-for-simplifying-and-improving-code.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Functional programming techniques for simplifying code</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/LzYJbEqCyC-300.avif 300w, https://anssi.dev/img/LzYJbEqCyC-600.avif 600w, https://anssi.dev/img/LzYJbEqCyC-900.avif 900w, https://anssi.dev/img/LzYJbEqCyC-1500.avif 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/LzYJbEqCyC-300.webp 300w, https://anssi.dev/img/LzYJbEqCyC-600.webp 600w, https://anssi.dev/img/LzYJbEqCyC-900.webp 900w, https://anssi.dev/img/LzYJbEqCyC-1500.webp 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/LzYJbEqCyC-300.png 300w, https://anssi.dev/img/LzYJbEqCyC-600.png 600w, https://anssi.dev/img/LzYJbEqCyC-900.png 900w, https://anssi.dev/img/LzYJbEqCyC-1500.png 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;My iMac&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/LzYJbEqCyC-300.jpeg&quot; width=&quot;1500&quot; height=&quot;1000&quot; srcset=&quot;https://anssi.dev/img/LzYJbEqCyC-300.jpeg 300w, https://anssi.dev/img/LzYJbEqCyC-600.jpeg 600w, https://anssi.dev/img/LzYJbEqCyC-900.jpeg 900w, https://anssi.dev/img/LzYJbEqCyC-1500.jpeg 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;Functional Programming can make your code simpler. Simplicity means that the code is easier to read and understand, testable, and more maintainable.&lt;/p&gt;
&lt;p&gt;In this blog post, I describe some of the Functional Programming (FP) tricks you can use to turn your code better by making it simpler.&lt;/p&gt;
&lt;h2&gt;Get rid of temp variables&lt;/h2&gt;
&lt;p&gt;Functional Programming favours immutability. This principle of immutability means that you don&#39;t change the values of your variables after you have initialised them. Similarly, you don&#39;t mutate your objects, or strings, after you have created them.&lt;/p&gt;
&lt;p&gt;If you are programming in JavaScript, all your variable definitions should use const. For anyone who is reading your code, the constant definition gives peace of mind: It guarantees that the variable is never reassigned. Because reassignment is impossible, the reader&#39;s brain is free from the burden of tracking and recognising reassignments from the code.&lt;/p&gt;
&lt;p&gt;What about when you need to change your values? At first, you could have an array:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fruits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;apple&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;orange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;banana&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What do you do when you want to add a new fruit into this list? The solution is to initialise a new constant variable with new or updated values. We can copy the existing fruits values using the spread operator:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allFruits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;fruits&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pineapple&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should follow the principle of immutability everywhere in your code. Because if you do, your code becomes cleaner.&lt;/p&gt;
&lt;p&gt;You know that the value of your fruits variable never changes, and you know this fact the first time you see the declaration of the variable. After the initialisation, anywhere you see the fruits variable, you know that it still has the same three fruits in it.&lt;/p&gt;
&lt;h2&gt;Get rid of loops&lt;/h2&gt;
&lt;p&gt;Functional code tells what happens instead of instructing how it should happen.&lt;/p&gt;
&lt;p&gt;Loops are not functional, and we should get rid of them.&lt;/p&gt;
&lt;p&gt;Consider the following loop:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; rows&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; row &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rows&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;childAddress&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;switchToBackupNode2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;edge&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    i &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The functional alternative gets rid of the while loop and uses map to process the rows into a list.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rows&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;childAddress&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;switchToBackupNode2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;edge&#39;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to getting rid of the loop, this code has also removed temporary variables row and message and the loop variable i.&lt;/p&gt;
&lt;p&gt;The result is much more readable.&lt;/p&gt;
&lt;p&gt;It&#39;s crystal clear.&lt;/p&gt;
&lt;p&gt;You can trust that this code works just by looking at it. To be fair here, to gain this level of trust, you need to understand the map function first. The map function is used everywhere in functional programming and thus learning it is an essential step in transitioning to FP.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; is one of the functions that programmers use for list processing. Processing data in lists is a big part of FP, and you should also learn the other list processing functions: most importantly, the reduce and filter functions.&lt;/p&gt;
&lt;h2&gt;Removing ifs&lt;/h2&gt;
&lt;p&gt;Removing ifs is a useful strategy in my guest of simplifying code. There are several strategies that one can take to remove if statements from code, and applying any of them usually results in cleaner, easier to understand, structures.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at some strategies for removing if statements.&lt;/p&gt;
&lt;h3&gt;Ternary Operator&lt;/h3&gt;
&lt;p&gt;The ternary operator is my main tool for getting rid of if statements from variable assignments.&lt;/p&gt;
&lt;p&gt;Consider the following:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;hello, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hey there!&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two problems in the code above:&lt;/p&gt;
&lt;p&gt;I must use a non-const variable message **because, with the if-structure, I am unable to give the message a value right away in the variable declaration.&lt;/p&gt;
&lt;p&gt;The code is quite a verbose and complex considering that it only accomplishes the simple task of declaring a variable and conditionally assigning a value to it.&lt;/p&gt;
&lt;p&gt;The same job can be done in one line by using the ternary operator &lt;code&gt;?&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;hello, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hey there!&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Boolean operators &amp;amp;&amp;amp; and ||&lt;/h3&gt;
&lt;p&gt;The boolean operators, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; provide effective alternatives to if statements.&lt;/p&gt;
&lt;p&gt;Instead of doing this,&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can do this:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;doStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The logical OR operator &lt;code&gt;||&lt;/code&gt; here provides a quick way to pass a default value to a function. You can use the same trick every time you don&#39;t know, or when it&#39;s not guaranteed, that a variable has a value: Provide a default value by prefixing  with || &lt;default&gt;.&lt;/default&gt;&lt;/p&gt;
&lt;p&gt;Here is an example of how to use the AND operator &amp;amp;&amp;amp; . First, the version using if:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sendData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we remove the if using &amp;amp;&amp;amp;:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we are using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; to fist check that the data variable holds a value. Without this check, the code would crash when there is no value (in other words, the value is null or undefined).&lt;/p&gt;
&lt;p&gt;We are relying on short-circuit evaluation of boolean expressions when using these operators her. With the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; operators, JavaScript will not evaluate the latter part of the expression when the first part is falsy. You can learn more about short circuiting here.&lt;/p&gt;
&lt;h2&gt;Maps and lookups&lt;/h2&gt;
&lt;p&gt;Using maps as lookup tables is an effective way to replace a sequence of if-statements. Consider the following:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; language&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;country &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;FI&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  language &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Finnish&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;country &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;SE&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  language &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Swedish&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;country &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;USA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  language &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;English (American)&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;country &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;UK&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  language &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;English (UK)&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// etc...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A cleaner way is to use a map of country/language pairs.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; languages &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FI&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Finnish&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SE&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Swedish&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;USA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;English (American)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;UK&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;English (UK)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; language &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; languages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;country&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second implementation is much simpler. The code reveals it&#39;s intention right away. As a bonus, it converts the language variable to use const&lt;/p&gt;
&lt;h2&gt;Small Functions&lt;/h2&gt;
&lt;p&gt;Functional Programming is all about functions. It&#39;s better to write many small functions instead of a small number of long functions. The main reason for decomposing code into smaller modules is that smaller (shorter) pieces are easier to understand in isolation. Smaller pieces are also easier to test and maintain.&lt;/p&gt;
&lt;p&gt;With the tricks I have shown here, your code can be more compact. That is an important goal and an excellent reason to learn these tricks, and more importantly, to learn functional programming.&lt;/p&gt;
&lt;p&gt;There are more tricks and strategies like this. Let me know if you would like me to write a post about the other strategies!&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Career Options for Ageing Developers</title>
			
			<link>https://anssi.dev/blog/career-options-for-ageing-developers.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>How to survive as a developer when you get older</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/SLILxkyo3H-300.avif 300w, https://anssi.dev/img/SLILxkyo3H-600.avif 600w, https://anssi.dev/img/SLILxkyo3H-900.avif 900w, https://anssi.dev/img/SLILxkyo3H-1500.avif 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/SLILxkyo3H-300.webp 300w, https://anssi.dev/img/SLILxkyo3H-600.webp 600w, https://anssi.dev/img/SLILxkyo3H-900.webp 900w, https://anssi.dev/img/SLILxkyo3H-1500.webp 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/SLILxkyo3H-300.png 300w, https://anssi.dev/img/SLILxkyo3H-600.png 600w, https://anssi.dev/img/SLILxkyo3H-900.png 900w, https://anssi.dev/img/SLILxkyo3H-1500.png 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Time glass running half empty&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/SLILxkyo3H-300.jpeg&quot; width=&quot;1500&quot; height=&quot;1000&quot; srcset=&quot;https://anssi.dev/img/SLILxkyo3H-300.jpeg 300w, https://anssi.dev/img/SLILxkyo3H-600.jpeg 600w, https://anssi.dev/img/SLILxkyo3H-900.jpeg 900w, https://anssi.dev/img/SLILxkyo3H-1500.jpeg 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;div class=&quot;lead&quot;&gt;
The half-life of a programmer is short. According to this [New York Times article from 1998](https://www.nytimes.com/1998/01/26/opinion/now-hiring-if-you-re-young.html), only 19% of programmers are still working as programmers 20 years after they started their career as a programmer.&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Six years after finishing college, 57 percent of computer science graduates are working as programmers; at 15 years the figure drops to 34 percent, and at 20 years — when most are still only in their early 40’s — it is down to 19 percent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This same phenomenon is still alive today. Programmers drift away from their original work and move to management and other roles that have little to do with the programming profession.&lt;/p&gt;
&lt;p&gt;I have always thought that this is sad. In my mind, management is something that almost anyone can do. It does not require any special skills or knowledge. Take a 2-month course in project management, and you will be able to do a reasonable job at it. Contrast that to the art of programming, which is a specialised skill that requires years of studying and practising before you can call yourself good at it.&lt;/p&gt;
&lt;p&gt;Sadly, some of the best programmers get “promoted” to management roles. In this blog post, I explain some of the benefits of staying hands-on with programming throughout your whole career.&lt;/p&gt;
&lt;h2&gt;Learning strategies&lt;/h2&gt;
&lt;p&gt;Things change a lot in the IT industry, and the career of an IT professional requires constant learning throughout the whole career.&lt;/p&gt;
&lt;p&gt;Let’s consider two different strategies that you can take when it comes to learning new technology during your career.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first strategy is to stay on the same technology platform and hone your skills in it.&lt;/li&gt;
&lt;li&gt;The second strategy is to take more significant leaps and choose to keep current with the latest development trends and innovation, regardless of where it happens.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;If you got it, flaunt it&lt;/h2&gt;
&lt;p&gt;Let’s consider the first option first. You have some hard-gained experience on platform X and you want to keep flaunting that. This platform might not be the most fashionable right at this moment. It’s not where the most action and innovation is happening right now. Take Java, and consider that as an example. Java is still the most popular language today, but one would argue that it’s no longer the most fashionable one. It’s not the place to be where the innovators are at the moment. But because it has been hugely popular for many years, it will undoubtedly be in high demand for 40 years to come. Because of this reason, you will not be out of work if you choose to continue working on the Java platform.&lt;/p&gt;
&lt;p&gt;Surprisingly, even COBOL which was created in 1959, continues to be relevant in some industry sectors. Apparently, &lt;a href=&quot;https://www.iceuro.com/cobol-programmers-in-demand&quot;&gt;Cobol developers are still in high demand&lt;/a&gt;. If you now Cobol and money is your primary motivator, it makes sense to continue and exploit this opportunity until it dries out. They are predicting that Cobol will stay a significant language for the next 20 years.&lt;/p&gt;
&lt;p&gt;There might be good money to be made if you stay on a legacy technology platform, but this could be a boring strategy to take. The chances are that you will not enjoy this! A better option would be to keep learning new things because that is a way to stay motivated in your work. Let’s explore that option next.&lt;/p&gt;
&lt;h2&gt;Learn something completely new&lt;/h2&gt;
&lt;p&gt;Some of us want to keep up with the latest technology. We get bored if we need to work with the current tools and frameworks for more than a year or two. We want to learn new programming languages, new &lt;a href=&quot;https://anssi.dev/post/learning-fp&quot;&gt;programming paradigms&lt;/a&gt;, and new tools and libraries.&lt;/p&gt;
&lt;p&gt;If you are one of the few, who has not given up for the temptation to become a Manager, you have likely kept on learning all the time, and that has kept you motivated to continue in a technical career.&lt;/p&gt;
&lt;p&gt;There is a &lt;a href=&quot;https://people.engr.ncsu.edu/ermurph3/papers/msr13.pdf&quot;&gt;study of StackOverflow data&lt;/a&gt; that explores the relationship between ageing and programming knowledge.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We observe that programmer reputation scores increase relative to age well into the 50’s&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;According to this study, your programming related knowledge increases with age. Programmers can learn and acquire new skills and knowledge well into the age of the 50 and older. Learning is not only a privilege of the young.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can move into a completely new technology platform if you want. It can require significant investments of time and effort to learn the latest technology, but rewards of learning can be marvellous&lt;/strong&gt;. You will be again in a place where the primary action is and where the bets of startups and innovating companies are. This kind of leaps in learning can be extremely motivating.&lt;/p&gt;
&lt;h2&gt;Go to Management&lt;/h2&gt;
&lt;p&gt;It feels like a promotion when you get an offer to be a manager. You have worked hard to become an excellent developer, and the management has noticed this. Your boss knows that you are the best in your team. One day there is a management position that the company need to fill, and they offer this position to you. What do you say? The job is challenging, and if you accept to take it, you will not have too much time for programming any more. Do you accept the offer and become a manager?&lt;/p&gt;
&lt;p&gt;I decided to say “yes” when my boss asked this question from me some 20 years ago. I never wanted to be a team leader, but still, I accepted the job. It feels like promotion, and as an ambitious person, it was too hard for me to reject the offer and challenge. That said, I have never regretted making this decision. I learned a lot by taking the time to learn Scrum and in doing my best to lead a development team.&lt;/p&gt;
&lt;p&gt;All the time, I tried to keep my hands dirty in programming. I had my side project with, and that kept me busy during my free time. Despite my efforts to stay hands-on with programming, I was not learning programming related things as much as before.&lt;/p&gt;
&lt;p&gt;One can argue that my programming career was in decline because of my decision to go to the dark side of management. The temptations of the dark side are the reason for the short half-life of programmers. If you stay in a management role for too long, and you can no longer compete with the young fresh-out-of-school developers that are coming to the workforce.&lt;/p&gt;
&lt;p&gt;In my case, things turned out good. I decided to leave my day job and start my own business, and that again involved a lot of coding.&lt;/p&gt;
&lt;h2&gt;Embrace the benefits related to your age&lt;/h2&gt;
&lt;p&gt;At the age of 40 or 50, you will have more time in your hands than before. Consider the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Your kids are already teenagers or adults. They might have moved out of the house already. , In any case, they are not spending much time with you any more.  This is great :-)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your marriage has gotten old as well, and your partner does not require much attention from you any more. This is also great.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;During your 20-30 years of professional life, you have gained a lot of expertise. You know about corporate politics, how businesses operate, how to do teamwork, how to recruit new people, how to train junior workers, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You have the &lt;em&gt;soft skills&lt;/em&gt; that the younger workers are still missing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this age would be the perfect time to start a business. According to research done in 2018, &lt;a href=&quot;https://hbr.org/2018/07/research-the-average-age-of-a-successful-startup-founder-is-45&quot;&gt;The Average Age of a Successful Startup Founder Is 45&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The founders of &lt;strong&gt;successful startups&lt;/strong&gt; are indeed this old, and we need to debunk the myth of the young entrepreneur.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Relative to founders with no relevant experience, those with at least three years of prior work experience in the same narrow industry as their startup were 85% more likely to launch a highly successful startup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I realise that the startup game is not everyone, but for anyone who has dreamed of having a business of their own, this could be the perfect age to pursue that dream. You have more time than ever in your hands to make it happen, and on top of that you also have the work experience to increase the likelihood of success.&lt;/p&gt;
&lt;p&gt;I started my first business at the age of 37. My open-source side project hobby started getting so popular that it made it possible for me to earn my income from it. I quit my day job and pursued my Flowplayer project full time as a business. This adventure as an entrepreneur lasted for ten years, but then, after some twists and turns, &lt;a href=&quot;https://anssi.dev/post/leafingflow&quot;&gt;I exited from Flowplayer two years ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today, I’m working as a Freelance developer and enjoying my work more than ever before. I’m again focusing almost entirely on programming.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Being a programmer is great, and your career can take many shapes and forms. You can continue programming for as long as you want. You have the potential to learn new skills and gains knowledge for as long as you need. Starting your own business, or switching technology platforms, are options that you can basically at any point during your career.&lt;/p&gt;
&lt;p&gt;Just do it and enjoy the ride!&lt;/p&gt;
&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/e6tIT5mh0--300.avif 300w, https://anssi.dev/img/e6tIT5mh0--600.avif 600w, https://anssi.dev/img/e6tIT5mh0--900.avif 900w, https://anssi.dev/img/e6tIT5mh0--1500.avif 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/e6tIT5mh0--300.webp 300w, https://anssi.dev/img/e6tIT5mh0--600.webp 600w, https://anssi.dev/img/e6tIT5mh0--900.webp 900w, https://anssi.dev/img/e6tIT5mh0--1500.webp 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/e6tIT5mh0--300.png 300w, https://anssi.dev/img/e6tIT5mh0--600.png 600w, https://anssi.dev/img/e6tIT5mh0--900.png 900w, https://anssi.dev/img/e6tIT5mh0--1500.png 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;Yours truly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/e6tIT5mh0--300.jpeg&quot; width=&quot;1500&quot; height=&quot;2000&quot; srcset=&quot;https://anssi.dev/img/e6tIT5mh0--300.jpeg 300w, https://anssi.dev/img/e6tIT5mh0--600.jpeg 600w, https://anssi.dev/img/e6tIT5mh0--900.jpeg 900w, https://anssi.dev/img/e6tIT5mh0--1500.jpeg 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Why you should learn Functional Programming</title>
			
			<link>https://anssi.dev/post/why-you-should-learn-functional-programming.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>Learning Functional Programming (FP) makes you a better programmer.</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/nc7O4WfipD-300.avif 300w, https://anssi.dev/img/nc7O4WfipD-600.avif 600w, https://anssi.dev/img/nc7O4WfipD-900.avif 900w, https://anssi.dev/img/nc7O4WfipD-1500.avif 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/nc7O4WfipD-300.webp 300w, https://anssi.dev/img/nc7O4WfipD-600.webp 600w, https://anssi.dev/img/nc7O4WfipD-900.webp 900w, https://anssi.dev/img/nc7O4WfipD-1500.webp 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/nc7O4WfipD-300.png 300w, https://anssi.dev/img/nc7O4WfipD-600.png 600w, https://anssi.dev/img/nc7O4WfipD-900.png 900w, https://anssi.dev/img/nc7O4WfipD-1500.png 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;My iMac&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/nc7O4WfipD-300.jpeg&quot; width=&quot;1500&quot; height=&quot;1125&quot; srcset=&quot;https://anssi.dev/img/nc7O4WfipD-300.jpeg 300w, https://anssi.dev/img/nc7O4WfipD-600.jpeg 600w, https://anssi.dev/img/nc7O4WfipD-900.jpeg 900w, https://anssi.dev/img/nc7O4WfipD-1500.jpeg 1500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The first 10 years of my programming career, I was doing Object-Oriented (OO) programming. I was working with Java mostly. I tried to master the art of OO and learned the design patterns to help me in coming up with class structures that avoid code duplication, and are flexible and adaptable for future change. My code was full of classes.&lt;/p&gt;
&lt;p&gt;Things started changing after I started programming in JavaScript, which did not force me to put all my code into classes. In my first JavaScript projects, the codebase still resembled the ones that I had been putting together with Java. I was not using classes, but despite this, some of the files still had a look&amp;amp;feel of a class. The code had modules that grouped functions that were somehow logically related to each other, just like a typical &amp;quot;service&amp;quot; class in Java would have.&lt;/p&gt;
&lt;p&gt;Today I have programmed in JavaScript for more than 5 years, and my coding has evolved a bit. I have started to think of my programs as data processors. There is always some data that needs to be processed. I think about what kind of processors and functions are needed to transform the data. My studying of Functional Programming has profoundly influenced me, and this data-centric approach arises from this studying and learning.&lt;/p&gt;
&lt;p&gt;In this blog piece, I explain why it has made sense for me to study FP and why you should also learn it.&lt;/p&gt;
&lt;h2&gt;The Benefits&lt;/h2&gt;
&lt;p&gt;Experienced functional programmers can tell you about the many benefits of functional programming:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functional code is easier to understand&lt;/li&gt;
&lt;li&gt;There are fewer bugs&lt;/li&gt;
&lt;li&gt;The code is more compact&lt;/li&gt;
&lt;li&gt;Some even claim that it&#39;s easier to test and debug&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I agree with these claims. The difference between paradigms is easy to see If we take a non-trivial programming challenge that has implementations both in a functional and in a traditional imperative style.&lt;/p&gt;
&lt;p&gt;Imperative implementations for a complex problem can be hairy with nested loops and nested if-then-else statements, class inheritance structures and all the things that we typically see in imperative programs.&lt;/p&gt;
&lt;p&gt;Have you ever studied a big object-oriented program that was done by an experienced OO practitioner? One that is well-factored to use classes with clear responsibilities. One that does not have any duplicated code and is DRY. Factoring code into classes with distinct, clear responsibilities removes code duplication. This kind of designs can include several hundreds of classes. It can be tough to see how this program works and how the different classes work during runtime.&lt;/p&gt;
&lt;p&gt;A well-factored functional implementation, on the other hand, might look scary when you first look at it, but after a little bit of studying, you should be able to understand the pieces (pure functions) that it has and how those are composed together.&lt;/p&gt;
&lt;p&gt;You can understand each function in isolation. You can trust that the program does what is promised.&lt;/p&gt;
&lt;h2&gt;Challenges&lt;/h2&gt;
&lt;p&gt;Contrary to what I just said in the paragraphs above, functional programs can be hard to understand for programmers that are not familiar with the functional style. Functional code can look quite different than the imperative counterpart. You cannot see many occurrences of elements that you are used to seeing in code: There are not many if statement, or for loops, for example.&lt;/p&gt;
&lt;p&gt;All you can see is a bunch of small functions and weird-looking compose(), and pipe() calls that might make no sense to you are not yet familiar with these concepts.&lt;/p&gt;
&lt;p&gt;There is a learning curve to understanding FP. First of all, you need to study the basics, and once you know the basics, you can start ramping up your knowledge little by little. There is a lot to learn before you are a master FP practitioner. I have been on this learning streak for one year now, and I&#39;m still at the beginning of my journey. I&#39;m sure that I&#39;ll reach the master-level status someday if I just continue working hard towards that goal.&lt;/p&gt;
&lt;p&gt;I have a 10-year history with object-oriented programming using Java. I was a huge fan of the &lt;a href=&quot;https://www.amazon.com/gp/product/B00794TAUG/ref=as_li_tl?camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B00794TAUG&amp;amp;ie=UTF8&amp;amp;linkCode=as2&amp;amp;linkId=bb0a52dca602b6618ceef609ac24e8aa&amp;amp;tag=anssipiiraine-20&quot;&gt;Eric Evans&#39; book Domain Driven Design&lt;/a&gt; and took it&#39;s teachings about Entities, Services and Value Objects seriously. I have implemented thousands of classes using the patterns explained in the Domain Driven Design and the GoF Design Patterns books. Because of this background of mine, FP made little sense to me when I first looked at it.&lt;/p&gt;
&lt;p&gt;I found debugging functional code to be challenging. Where do you add your breakpoints, when all you have is a list of functions linked together using pipe? Debugging is another area where you need to learn new tricks because the old ones don&#39;t work with FP. Luckily there are &lt;a href=&quot;https://itnext.io/debugging-functional-javascript-545b6ea59660&quot;&gt;some good strategies&lt;/a&gt; for debugging functional code.&lt;/p&gt;
&lt;h2&gt;Learning Functional Programming&lt;/h2&gt;
&lt;p&gt;Why would you take the effort and learn FP? I guess the most important reason is that learning it makes you a better programmer. You can benefit from knowing about FP even if you continue doing object-oriented programming. Making functions pure and favouring immutability are great habits, no matter what your primary programming paradigm and programming language are.&lt;/p&gt;
&lt;p&gt;You don&#39;t need to go full-on with functional and scare your co-workers by coming up with code that is so full of currying that you need a degree in Indian cuisine to understand it. I would recommend Kyle Simpson&#39;s book Functional-Light JavaScript to get you started. It provides a pragmatic, balanced way of doing FP in your JavaScript projects. It is the book that got me started with FP.&lt;/p&gt;
&lt;p&gt;Ramda is an excellent functional toolkit library for JavaScript. You need something like it to ease your life. It contains the functional &amp;quot;primitives&amp;quot; that you can start using in your real-life projects.&lt;/p&gt;
&lt;p&gt;Mastering FP takes time. I&#39;m at the beginning of my journey, and my journey so far has looked like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I read the Functional-Light JavaScript book and got excited&lt;/li&gt;
&lt;li&gt;I started sprinkling in FP to my daily projects. I started making my functions pure and started avoiding mutating my data.&lt;/li&gt;
&lt;li&gt;But then I got busy with daily projects, and my FP learning ambitions got sidelined...&lt;/li&gt;
&lt;li&gt;Then luckily, I became less busy again, and my FP learning was back on track.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Summing up&lt;/h2&gt;
&lt;p&gt;I think it&#39;s essential always to study and learn more. In this profession, it&#39;s a must. If you stop learning and improving, more ambitious programmers overshadow and take over you. Eventually, you give up programming and become a Manager and, for sure, that is a scenario that every self-respecting programmer wants to avoid :-)&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>Building an Electron application</title>
			
			<link>https://anssi.dev/post/building-an-electron-application.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>My experience on developing with Electron.js</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/vXYwPao6vQ-300.avif 300w, https://anssi.dev/img/vXYwPao6vQ-600.avif 600w, https://anssi.dev/img/vXYwPao6vQ-900.avif 900w, https://anssi.dev/img/vXYwPao6vQ-2500.avif 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/vXYwPao6vQ-300.webp 300w, https://anssi.dev/img/vXYwPao6vQ-600.webp 600w, https://anssi.dev/img/vXYwPao6vQ-900.webp 900w, https://anssi.dev/img/vXYwPao6vQ-2500.webp 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/vXYwPao6vQ-300.png 300w, https://anssi.dev/img/vXYwPao6vQ-600.png 600w, https://anssi.dev/img/vXYwPao6vQ-900.png 900w, https://anssi.dev/img/vXYwPao6vQ-2500.png 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;A rubber duck&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/vXYwPao6vQ-300.jpeg&quot; width=&quot;2500&quot; height=&quot;2500&quot; srcset=&quot;https://anssi.dev/img/vXYwPao6vQ-300.jpeg 300w, https://anssi.dev/img/vXYwPao6vQ-600.jpeg 600w, https://anssi.dev/img/vXYwPao6vQ-900.jpeg 900w, https://anssi.dev/img/vXYwPao6vQ-2500.jpeg 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;Back in March this year (2019), I stumbled upon a technology called &lt;em&gt;Electron&lt;/em&gt;, which offers a new way of building
cross-platform desktop applications. The first version of Electron appeared already in 2013, but somehow I had missed
entirely that release (probably because at that time I was 110% occupied with Flowplayer).&lt;/p&gt;
&lt;p&gt;I had always been interested in building desktop applications without ever doing that as my primary job. I had
experimented with Mac development using Objective-C and XCode but did not advance far with those experiments.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I ended up trying Electron because I wanted to know how I could use my React.js and Node.js skills in desktop development.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I had also stumbled into &lt;a href=&quot;https://github.com/notable/notable&quot;&gt;Notable&lt;/a&gt; which is a &amp;quot;Markdown-based note-taking app that
doesn&#39;t suck&amp;quot;. I studied its code base and became convinced that this development platform has enough going on and I
should try it out.&lt;/p&gt;
&lt;p&gt;What is Electron?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Electron allows for the development of desktop GUI applications using web technologies: It combines the Chromium rendering engine and the Node.js runtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;cite&gt;Wikipedia/Electron&lt;/cite&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;React and other frameworks&lt;/h2&gt;
&lt;p&gt;I wanted to try if I could build an application like &lt;a href=&quot;https://www.getpostman.com/&quot;&gt;Postman&lt;/a&gt;. I had used Postman and also
a similar Mac app called Paw earlier. I had some thoughts on how these applications could be better and
wanted to try those ideas in my app.&lt;/p&gt;
&lt;p&gt;I had been working with React since the summer of 2018. I had built a sizable app using it and was already quite
familiar with it. That&#39;s why I wanted to use it in this new project. I started searching for React component frameworks
that would be good for the desktop - the components would need to a desktop-like look and feel. After some research, I
ended up using &lt;a href=&quot;https://blueprintjs.com/&quot;&gt;Blueprint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I like &lt;a href=&quot;https://www.styled-components.com/&quot;&gt;the styled components&lt;/a&gt; library, which is a library that allows writing CSS as
JavaScript template strings. My goal was to produce something usable quickly (like most of us want), and that&#39;s why I
wanted to utilise existing code/libraries/frameworks to get up-to-speed quickly.&lt;/p&gt;
&lt;h2&gt;Productivity&lt;/h2&gt;
&lt;p&gt;The Electron and React combo is a killer platform when it comes to productivity. Indeed, web technologies are ahead of
the tech that is currently used in traditional desktop development. To me, using a component library like Blueprint is a
blessing: I can quickly look through their examples and pick the components I need to build any UI feature. The UI
components are flexible, and I can change their styling and size the way I need. If something does not look the way I
want out-of-the-box, I can pretty quickly resort to custom CSS to change the default look the component has.&lt;/p&gt;
&lt;p&gt;It&#39;s not a surprise that the code Apple&#39;s &lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot;&gt;SwiftUi&lt;/a&gt; looks a lot like in
React&#39;s or Flutter&#39;s component-based systems.&lt;/p&gt;
&lt;p&gt;Electron apps are cross-platform, and you can build your app from the same code base for Linux, Windows and Mac.&lt;/p&gt;
&lt;h2&gt;What did I learn?&lt;/h2&gt;
&lt;p&gt;I have been primarily a backend developer for most of my career. I accumulated most of my frontend web development
experience in the past two years. This project was a good boost of confidence, making me realise that I can indeed work
also on the frontend side. I ended up also developing a &lt;a href=&quot;https://dispatch.rest/&quot;&gt;website for my app&lt;/a&gt;, which was another
great experience.&lt;/p&gt;
&lt;p&gt;I am now reasonably confident that I can build anything from start to finish. I could do it entirely on my own if I
wanted to  :-) I am convinced that I can come up with the backend, the frontend, the website, and marketing material.
This realisation makes me think of the next idea for a startup I should bootstrap. Meanwhile, before the big idea
emerges, I will continue working on this Dispatch project.&lt;/p&gt;
&lt;p&gt;In our summer cottage in Lapinsaari, Savonlinna, Finland&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
		<item>
			<title>I&#39;m leaving Flowplayer</title>
			
			<link>https://anssi.dev/blog/i&amp;#39;m-leaving-flowplayer.html</link>
			<updated>2025-06-20T00:00:00Z</updated>
			<description>How I ended up leaving the company I founded and built</description>
			<content type="html">&lt;div class=&quot;image-container overflow-hidden relative h-96 aspect-w-16 aspect-h-9&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://anssi.dev/img/mQUL9N-mvg-300.avif 300w, https://anssi.dev/img/mQUL9N-mvg-600.avif 600w, https://anssi.dev/img/mQUL9N-mvg-900.avif 900w, https://anssi.dev/img/mQUL9N-mvg-2500.avif 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://anssi.dev/img/mQUL9N-mvg-300.webp 300w, https://anssi.dev/img/mQUL9N-mvg-600.webp 600w, https://anssi.dev/img/mQUL9N-mvg-900.webp 900w, https://anssi.dev/img/mQUL9N-mvg-2500.webp 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://anssi.dev/img/mQUL9N-mvg-300.png 300w, https://anssi.dev/img/mQUL9N-mvg-600.png 600w, https://anssi.dev/img/mQUL9N-mvg-900.png 900w, https://anssi.dev/img/mQUL9N-mvg-2500.png 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;img alt=&quot;My iMac&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;block object-contain max-h-full max-w-full&quot; src=&quot;https://anssi.dev/img/mQUL9N-mvg-300.jpeg&quot; width=&quot;2500&quot; height=&quot;1875&quot; srcset=&quot;https://anssi.dev/img/mQUL9N-mvg-300.jpeg 300w, https://anssi.dev/img/mQUL9N-mvg-600.jpeg 600w, https://anssi.dev/img/mQUL9N-mvg-900.jpeg 900w, https://anssi.dev/img/mQUL9N-mvg-2500.jpeg 2500w&quot; sizes=&quot;100vw&quot; /&gt;&lt;/picture&gt;&lt;/div&gt;
&lt;p&gt;I&#39;ve resigned from Flowpalyer, the company I founded back in 2007. The decision to leave was not easy to make. Flowplayer has been my number one priority for ten years, and I&#39;m finding it difficult to let it go, but still, I think it&#39;s the right choice for me.&lt;/p&gt;
&lt;p&gt;Flowplayer merged with a Swedish online video company a year ago, and after that, my role has significantly changed. I used to be the CEO and also a developer (and a sysadmin, a secretary, and everything in between) and did my best to keep the company profitable and growing. The merge gave us more resources, and I stepped down from the CEO role and also gave much of my other responsibilities to other people in the company. That is something I wanted to do, and I don&#39;t have any regrets about it. But after successfully offloading many of my duties I started feeling a bit empty and began considering my options to take on some new opportunities.&lt;/p&gt;
&lt;p&gt;I&#39;ve always been a developer in my heart. Programming has been the only kind of work that is fulfilling for me and activities, like management and similar paper-pushing sort of work, has never felt like &amp;quot;real work&amp;quot; to me. After the merge, I was able to move away from the dark side of management and was able to concentrate more on work that is meaningful for me.&lt;/p&gt;
&lt;p&gt;Since our merger, I have been on a mission to learn all the latest technology and honing my JavaScript and node.js skills. All this has been refreshing and highly motivating. It has been great to get back my mojo in programming -- I&#39;m more productive as a coder now, and I think I&#39;m also producing better-quality results.&lt;/p&gt;
&lt;p&gt;What is the real reason that pushed me to resign? Here is a list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&#39;ve been working in Flowplayer for ten years, and it&#39;s time to explore other stuff. It has been the longest streak I have ever been in the same company. Before Flowplayer, my most extended period in one job was 3,5 years.&lt;/li&gt;
&lt;li&gt;I think Flowplayer is in good hands and can move forward and grow without me.&lt;/li&gt;
&lt;li&gt;My plans for the upcoming projects are exciting and highly motivating.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My opinion is that if you start questioning whether you should move forward, it&#39;s time to move forward. Change can be difficult, but it&#39;s a way to grow as a professional and as a person. I&#39;ve changed jobs several times in my life, and every change has been for the better.&lt;/p&gt;
&lt;p&gt;I&#39;ve decided to continue as a freelance developer, and see where it takes me. I&#39;ve already started planning my next steps in this space and will post updates here in my blog about this. Having an online presence is essential for a freelance worker, and I&#39;m planning to post more about work-related topics in the future.&lt;/p&gt;
</content>
			<author>
				<name>Anssi Piirainen</name>
				<email>api@iki.fi</email>
			</author>
		</item>
		
	</channel>
</feed>
