<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tim Severien - posts</title><description>Blog of another senior web engineer</description><link>https://tsev.dev/</link><language>en-gb</language><item><title>Techniques for sampling colour in creative coding</title><link>https://tsev.dev/posts/2026-03-27-techniques-for-sampling-colour-in-creative-coding/</link><guid isPermaLink="true">https://tsev.dev/posts/2026-03-27-techniques-for-sampling-colour-in-creative-coding/</guid><pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate><category>creative-coding</category><author>Tim Severien</author></item><item><title>Looping over indexOf() matches</title><link>https://tsev.dev/posts/2026-02-10-looping-over-indexof-matches/</link><guid isPermaLink="true">https://tsev.dev/posts/2026-02-10-looping-over-indexof-matches/</guid><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The other day, I was writing an &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc4180.html&quot;&gt;RFC 4180&lt;/a&gt;-compliant CSV parser for fun. This file format uses delimiters to separate fields and records (i.e. columns and rows respectively). A field (i.e. cell) may contain one of these delimiters, so parsing is slightly more involved than a &lt;code&gt;string.split()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In my first and naive implementation, I read over every character as I wrote it to the buffer. This allowed me to keep track of a simple state machine and flush the fields and records the moment a delimiter is hit. In the vast majority of these iterations, the symbol isn’t a delimiter, so it only gets appended to the buffer. That is very wasteful and noticeable in the benchmark where I compared it to other CSV parsers.&lt;/p&gt;
&lt;p&gt;A more top-down approach is to jump directly between potential delimiters in the buffer instead of inspecting every character. For every candidate, we can do more analysis to figure out whether it’s part of a field’s content or a true delimiter.&lt;/p&gt;
&lt;p&gt;This is a great example of something you want to do iteratively. A buffer may or may not contain a delimiter, we have to know the delimiter’s location to be able to do more analysis, and looking up all matches when the first is likely good enough is wasteful. The iterative approach and desire to stop early makes this a great use-case for generator functions!&lt;/p&gt;
&lt;p&gt;We can write a generic utility function that is useful beyond parsing CSV files to loop over &lt;code&gt;indexOf()&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;Here’s a simplified/less optimised version of what I’m using:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function* indexOfAll(string: string, substring: string): Generator&amp;lt;number&amp;gt; {
	// Early return for cases where we&apos;re not going to find any matches
	if (string.length === 0 || substring.length === 0 || substring.length &amp;gt; string.length) {
		return;
	}

	let index = -1;
	let offset = 0;

	// 1. Get the index of the first occurrence of `substring` in `string`, starting at `offset`
	// 2. Assign `indexOf()` return value to `index`
	// 3. Loop while `index &amp;gt; -1`
	while ((index = string.indexOf(substring, offset)) &amp;gt; -1) {
		yield index;

		// For next iteration, we want to start looking after current match, so set offset accordingly
		offset = index + substring.length;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we can use that in a for...of loop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const string = &apos;Hello world&apos;;
const substring = &apos;l&apos;;

for (const index of indexOfAll(string, substring)) {
	console.log(index); // 2, 3, 9

	// If we&apos;re not interested in other results, we can `break` out of the loop
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not nearly enough to get my CSV parser up to par with alternatives, but it’s a small, reusable optimization that removes work from a hot loop.&lt;/p&gt;
</content:encoded><category>js</category><author>Tim Severien</author></item><item><title>Thoughts on artificial intelligence</title><link>https://tsev.dev/posts/2026-01-13-artificial-intelligence/</link><guid isPermaLink="true">https://tsev.dev/posts/2026-01-13-artificial-intelligence/</guid><pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Artificial intelligence (AI) sparks a lot of discussion and strong feelings in my circles, creating two vocal camps. You’re either for or against. Unlike the people in these camps, my sentiment about AI has been going back-and-forth, and it took me a while to figure out why. One day I’d be excited about possibilities, and the next day I didn’t want anything to do with AI. It turns out, different aspects of the technology invoke different feelings.&lt;/p&gt;
&lt;p&gt;In this post, I want to look at AI from different perspectives and share my thoughts on each.&lt;/p&gt;
&lt;h2&gt;AI as a technology&lt;/h2&gt;
&lt;p&gt;It must’ve been around 2019 when I started to actively follow developments in machine learning and neural networks. The concept of function approximation by having an algorithm optimise a model that transforms inputs to outputs was and is incredibly exciting. To really get how things worked, I followed courses, coded my own neural network from scratch, and dabbled with TensorFlow and Keras in small projects.&lt;/p&gt;
&lt;p&gt;I still believe it’s transformative technology that lets us solve many problems we considered too complex or impractical to solve before, so the excitement is still there, but that excitement now comes with a degree of discomfort.&lt;/p&gt;
&lt;h2&gt;AI as an industry&lt;/h2&gt;
&lt;p&gt;A part of the industry deserves praise. Many people build meaningful systems, like tools that help doctors make more accurate diagnoses, systems that improve farming efficiency and lower emissions, and machinery that is more accurate in measuring the quality of produce and saves waste. I can’t get enough of applications that measurably benefit humankind.&lt;/p&gt;
&lt;p&gt;The reality, unfortunately, is that not every endeavour is a clear positive. To develop large language models (LLMs), various companies blatantly rip most of humanity’s creative works, extort and traumatise low-wage workers to label the data, somehow manage to redirect a huge amount of resources from (sometimes publicly-funded) civilian infrastructure, and then get filthy rich off of it. Or rather, get filthy rich off promises and investments. It takes hundreds of billions[^economy] worth of investments to buy the data they couldn’t steal, build the data centers, purchase the hardware, and operate the whole show.&lt;/p&gt;
&lt;p&gt;So naturally, these achievements and promises are overblown because the money needs to keep flowing in. Artificial general intelligence? Consciousness? Some of these LLMs still struggle with counting the number of r’s in strawberry. Surely we’re several major advancements away from getting even close. Until then, just throwing more data at it yields diminishing returns.[^exponential-data]&lt;/p&gt;
&lt;p&gt;Consequently, I believe AI is a bubble, though primarily a financial one. I’m sure the technology is here to stay, it’s just grossly overvalued and implemented excessively. For the sake of our wallets, let’s hope the bubble doesn’t burst too violently and doesn’t trigger a recession.&lt;/p&gt;
&lt;p&gt;I also worry about how this technological shift disempowers workers and further concentrates wealth to a very small group of people in tech. Perhaps I’d find forgiveness in my heart if they’d promote social safety nets to prepare us for their automated utopia. But they often promote hypercapitalism, in which the unoccupied are homeless and hungry.&lt;/p&gt;
&lt;p&gt;All in all, there’s a lot wrong with the industry, more than I mentioned, and it’s a large contributor to my conflicting feelings about AI. I hope there’s a future where AI is ethically sourced, doesn’t gobble up resources mankind needs to survive, and doesn’t disrupt the entire working world without offering a better alternative.&lt;/p&gt;
&lt;h2&gt;AI as an assistant&lt;/h2&gt;
&lt;p&gt;On the consumer side of things, we have LLM assistants. They have their use, but the impact of using them is opaque. Attempts to calculate energy and drinking water consumption by AI varies wildly, but it’s likely tremendous[^consumption] both for training the models and inference. With ChatGPT having over 700 million weekly users,[^user-count] the total impact is tremendous either way. And for what? Homework and &lt;a href=&quot;https://en.wikipedia.org/wiki/AI_slop&quot;&gt;slop&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;With so many eyeballs pointed to assistants instead of apps and websites, obviously the next move is to serve advertisements.[^ads] We’ve seen how that goes. Ad platforms gobble up data to serve personalized ads and they’ll trick us into watching more so that more money can be made. Maybe they’ll take a slice off in-assistant purchases, a reason to incentivise spending money as well. Look, ads aren’t new, and I’m all for sustainable revenue models, but having ads served by what people believe to be magical truth boxes that regularly and confidently output false information, is bound to give more issues than banners.&lt;/p&gt;
&lt;p&gt;We definitely need legislation to be updated to the AI assistants era, as it’s happy to give us financial advice, pretend to be a psychologist, or will try to sell you a product, but without the accountability.&lt;/p&gt;
&lt;h2&gt;AI as a geopolitical race&lt;/h2&gt;
&lt;p&gt;Clearly, the AI race is on. Not only between competing companies, but also between nations. As a multi-billion dollar industry[^economy], there’s much to gain economically by coming out on top. It’s also a powerful tool for military purposes. Or for domestic control. Or to sway public opinion and interfere with elections, both domestically as foreign. Everyone wants a piece of that pie.&lt;/p&gt;
&lt;p&gt;It would be inaccurate and cynical of me to say that, because of the above, governments aren’t doing anything that inhibits the AI industry to thrive. That’s not to say the AI industry doesn’t lobby and doesn’t influence policy, but something &lt;em&gt;is&lt;/em&gt; being done. We have the EU AI Act, which prohibits harmful uses of AI, introduces obligations for “high-risk” uses, and introduces some transparency rules.[^ai-act] While I have plenty to say about wealth accumulation and resource use in the AI industry, these aren’t exclusive to the AI space, and perhaps we shouldn’t mitigate that through legislation. At the risk of slowing down innovation and giving away the first spot, I’d love to see governments take these issues more seriously.&lt;/p&gt;
&lt;h2&gt;AI as a tool in software engineering&lt;/h2&gt;
&lt;p&gt;As you’ve read, I have plenty of gripes with the AI industry, and it made me hesitant to start to use AI in my work, but I eventually took the plunge when pushed gently.&lt;/p&gt;
&lt;p&gt;I started out using open-source models trained on public data first. At the time, these weren’t good enough to make it worthwhile. My mileage was poor compared to that of my peers, who used the latest, greatest, and largest models offered. Despite my reservations, I eventually started to use the Cursor license I was given, but once in a while I come back to open-source models. Surely, at some point, we’ll get ethically sourced, maybe even resource efficient models that are competitive.&lt;/p&gt;
&lt;p&gt;I’ve come to understand why some engineers feel their work is taken away from them. My love for coding started out as just that: a love for coding. AI takes that mechanical work away. Over the years, my interest shifted towards problem solving and engineering enablement, so I’m not too upset about offloading the manual labour. In some ways, it’s akin to delegating work to other developers, except AI doesn’t need to be motivated, doesn’t need breaks, has an incredibly broad knowledge of technologies, has no ego, but it can be utterly wrong with confidence. Because it’s not human, I’ll dismiss faulty work by AI without hesitation, whereas addressing a bad pull request from a junior developer requires tact. Delegating work to AI feels simpler, but it’s also less fun and engaging.&lt;/p&gt;
&lt;p&gt;This analogy, however, creates the expectation that a senior engineer’s output multiplies by some amount because they have a virtual team of agents. This doesn’t exactly match my experience, but I’m still learning how to get most out of AI. That expectation does devalue our work. Have you ever received a raise proportionate to a productivity increase?&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- On the long term, there’s the unresolved issue of how we’re supposed to train engineers. If I’d have to make a guess, I think future engineers will just be different. the existing split between product-oriented and technology-oriented folk widens. Product engineers will need less technological expertise and will make do with surface-level knowledge of a wide array of tools to solve day-to-day problems. Perhaps this role then becomes a hybrid of user experience design and software architect. Maybe we’ll get software architect prompt engineers with domain expertise so that they’re better equipped to describe desired output. I’m not sure. --&amp;gt;&lt;/p&gt;
&lt;p&gt;Is there a way back? Even if we lose knowledge along the way, we document our knowledge well enough for it to be learned again, but I think it’s unlikely that we need to go back. Once the bubble bursts, or when investors want a sustainable revenue model, I expect price hikes. If that happens tomorrow, at a point where many engineers are still sceptical, maybe we’ll see adoption and thus investments and thus developments stagnate. If it happens later, at a point when we’re confident that coding assistants boosts productivity by some amount, price hikes can be overcome. A tool that doubles productivity and costs less than a salary, is good deal, and today, a Premium Claude seat is 25-50× cheaper than a salary for a senior software engineer.&lt;/p&gt;
&lt;p&gt;So, what does moving forward look like? I have high hopes that we’ll see more activity in the class of programming languages that focuses on defining outlines, specifications, validation, and proofs. Today, we can write a suite of unit tests and have AI write passable code of the implementation. If my tests are comprehensive enough, I trust the implementation through validation. Now imagine a language tailored to writing tests so we can write and review these more efficiently and without the syntactical baggage of an arbitrary programming language. We’ll need to figure out what level of granularity we’re comfortable with. Maybe some applications have few technological requirements and can be written as user flows, like in &lt;a href=&quot;https://en.wikipedia.org/wiki/Cucumber_(software)#Gherkin_language&quot;&gt;Gherkin&lt;/a&gt;, while other programs require more in-depth specification and/or validation. For example, for embedded software, we probably want to keep some control over memory usage. I suspect we’ll move from writing implementations to writing specifications that machines can satisfy.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;Moving into the AI-era both worries and excites me.&lt;/p&gt;
&lt;p&gt;Many of my friends shun everything AI, often for similar reasons mentioned in this post. I appreciate their unwavering beliefs and, as you can tell, I share their concerns and I hope governments and the space start working towards solutions.&lt;/p&gt;
&lt;p&gt;But the uncertainty of the future brings out the curious nerd in me that wants to learn, explore, and adapt. My job is to stay on top of technology. It keeps me useful, productive, and employable. From my perspective, AI is here to stay, so it’s something I need to be on top of.&lt;/p&gt;
&lt;p&gt;Although I cannot exert a lot of influence, I can try to mitigate some of the bad parts as I’m learning to use these tools. Instead of throwing AI at every problem that can be solved by other means, I choose to be conservative. I can discourage the creation of most wasteful and least useful use of AI by avoiding social media slop and asking others to do so as well. Instead of rewarding AI companies that train models with stolen data, I can deliberately support (open-source) models that are exclusively trained on public domain data.&lt;/p&gt;
&lt;p&gt;[^ads]: &lt;a href=&quot;https://www.testingcatalog.com/openai-plants-testing-ads-in-chatgpt-android-app/&quot;&gt;OpenAI plants testing ads in ChatGPT Android app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^ai-act]: &lt;a href=&quot;https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai&quot;&gt;AI Act&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^consumption]: &lt;a href=&quot;https://arxiv.org/pdf/2505.09598v2&quot;&gt;How Hungry is AI? Benchmarking Energy, Water, and Carbon Footprint of LLM Inference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^economy]: &lt;a href=&quot;https://hai.stanford.edu/ai-index/2025-ai-index-report/economy&quot;&gt;Artificial Intelligence Index Report 2025, Economy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^exponential-data]: &lt;a href=&quot;https://arxiv.org/abs/2404.04125&quot;&gt;No &quot;Zero-Shot&quot; Without Exponential Data: Pretraining Concept Frequency Determines Multimodal Model Performance&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^user-count]: &lt;a href=&quot;https://www.cnbc.com/2025/08/04/openai-chatgpt-700-million-users.html&quot;&gt;OpenAI’s ChatGPT to hit 700 million weekly users, up 4x from last year&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>tools</category><author>Tim Severien</author></item><item><title>Safe environment variables in JavaScript</title><link>https://tsev.dev/posts/2025-12-03-safe-environment-variables-in-javascript/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-12-03-safe-environment-variables-in-javascript/</guid><pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Who hasn’t accidentally deployed code that depended on a secret or environment variable that wasn’t configured for that environment? I know I have.&lt;/p&gt;
&lt;p&gt;Environment variables are often critical for software to work. Sometimes in obvious ways, like making sure the browser is sent to the correct authentication URL, or that the right URLs are set in the Content Security Policy header. Sometimes, they’re not so obvious. Maybe nothing gets sent to our analytics service because we’re missing an ID. Maybe a map widget, buried in a multi-page form, isn’t loading because we’re missing a key. Perhaps everything is fine except for completing the purchase because we forgot the production token for our payment provider. Whoops!&lt;/p&gt;
&lt;p&gt;Stupidly enough, many environments (like Node.js) don’t offer built-in tools to register, validate, and parse environment variables. Consequently, few codebases do so and most only have a reference to these variables when needed.&lt;/p&gt;
&lt;p&gt;In short, missing variables can cause runtime issues from critical to negligible, and from obvious to invisible. That’s an incredible amount of uncertainty that many teams blatantly accept.&lt;/p&gt;
&lt;p&gt;We can make these issues more visible with very little effort. I often introduce an &lt;code&gt;env.ts&lt;/code&gt; file to my JavaScript codebases. It’s a file where we validate and parse all environment variables with Zod and export the parsed object, giving us many benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a variable is missing or invalid, we get early feedback&lt;/li&gt;
&lt;li&gt;Environment variable validation is centralised&lt;/li&gt;
&lt;li&gt;With Zod, we can add additional constraints&lt;/li&gt;
&lt;li&gt;We get proper types throughout the codebase&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s an example of an &lt;code&gt;env.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &apos;zod&apos;;

const envSchema = z.object({
	ANALYTICS_ID: z.string().nonempty(),
	ANALYTICS_URL: z.httpUrl(),
	API_URL: z.httpUrl(),
	APP_PORT: z.coerce.number().int().positive().default(4321),
	MAP_TOKEN: z.string().nonempty(),
});

export const env = envSchema.parse(process.env);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can import this file and use any variable with confidence and without last-minute validation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { env } from &apos;@/env&apos;;

// ...

// env.APP_PORT is a number; we can safely increment it
server.listen(env.APP_PORT);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To ensure everything is validated and parsed at startup, we can add a side-effect import declaration in the application’s entry file (e.g. &lt;code&gt;index.ts&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import &apos;@/env&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we want, we can ensure no &lt;code&gt;process.env&lt;/code&gt; is being used outside of &lt;code&gt;env.ts&lt;/code&gt; with a small script that searches the codebase using &lt;a href=&quot;https://github.com/BurntSushi/ripgrep&quot;&gt;ripgrep&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
query=&apos;process.env&apos;
exclude=&apos;!src/env.ts&apos;

if [[ $(rg $query -g $exclude --type js --type ts ) ]]; then
	echo &quot;process.env is being used outside src/env.ts. Fix it!&quot;
	exit 1
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like that, no more runtime errors due to missing environment variables. The solution does have some caveats, though.&lt;/p&gt;
&lt;p&gt;Depending on the environment we’re targeting, things can get finicky. Browsers don’t have access to environment variables, so many bundlers replace lookups with their actual values at build time. For example, &lt;code&gt;process.env.NODE_ENV&lt;/code&gt; is often replaced with &lt;code&gt;&quot;production&quot;&lt;/code&gt; for browser bundles. For these, we’ll have to explicitly add these lookups so the bundler knows what to include. Depending on volume and complexity, I sometimes opt for a dedicated &lt;code&gt;env.browser.ts&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Explicitly add variables so bundlers can replace values
export const env = browserEnvSchema.parse({
	NODE_ENV: process.env.NODE_ENV,
	PUBLIC_ANALYTICS_ID: process.env.PUBLIC_ANALYTICS_ID,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take into account that we want to use flags over optional variables. If we only want analytics on production, and choose to make &lt;code&gt;ANALYTICS_ID&lt;/code&gt; optional, we lose the early warning sign when &lt;code&gt;ANALYTICS_ID&lt;/code&gt; is missing unintentionally. Introducing a flag (e.g. &lt;code&gt;ANALYTICS_ENABLED&lt;/code&gt;) ensures that this cannot happen, even if it means &lt;code&gt;ANALYTICS_ID&lt;/code&gt; is unused and may have a dummy value.&lt;/p&gt;
&lt;p&gt;Overall, these caveats are worth it. Registering variables is a minor effort that pays itself back tenfold by turning silent failures into loud, early errors.&lt;/p&gt;
</content:encoded><category>js</category><author>Tim Severien</author></item><item><title>My cats are family</title><link>https://tsev.dev/posts/2025-09-29-my-cats-are-family/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-09-29-my-cats-are-family/</guid><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Content warning: dead pet, grief.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For all my life, I’ve lived with pets, mainly dogs and cats. I’ve fed them, washed them, walked them, played with them, cuddled with them, loved them.&lt;/p&gt;
&lt;p&gt;That love becomes painfully obvious when you lose them. Recently, we unexpectedly lost one of our two cats. One minute he was fine and playing. The next minute he wasn’t.&lt;/p&gt;
&lt;p&gt;As we shared what happened, we’ve had two types of responses. Some people deeply understand our pain and grief, and some just don’t. My guess is that the latter never lived with pets or believe that anything non-human is lesser, maybe undeserving of our love. Whatever the reason, their expectation of how we feel and how we actually feel are light-years apart. To bridge that gap, let me shed some light on what our companions mean to us.&lt;/p&gt;
&lt;p&gt;Our cats are family. We eat together, we wash together, we play together, we chill together, we sleep together. With me being a (mostly) remote worker, we work together, though I’m doing all of the work. The moment we set foot out of the house, we crave returning to our beloved family. I see and interact with our cats more than many do with their friends, their families, their spouses, perhaps even their children.&lt;/p&gt;
&lt;p&gt;Consequently, my cat’s absence is very present. Every room, every surface, every corner is full of memories. Our cat had personality. He would jump in the shower before us, demanding cuddles. Now he can’t. He would barge in meowing when I rinsed after brushing my teeth. Now it’s just dead silence. His favourite spot, our go-to spot to unwind and share and receive love, is empty now.&lt;/p&gt;
&lt;p&gt;Some mornings he’d wake us up early. To let my partner sleep in, I’d walk him into the back yard where he’d announce his presence to the neighbourhood with boisterous meows. We miss him, his personality, his quirks, even the things we thought we hated.&lt;/p&gt;
&lt;p&gt;So, of course we can’t pretend everything is fine the next day. We’ve lost a loved one, one of my closest friends, a member of the family.&lt;/p&gt;
&lt;p&gt;If you’ve never experienced a bond like that, please don’t dismiss our grief. The love is real, and so is our grief.&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>Glassmorph responsibly: test the worst case scenarios</title><link>https://tsev.dev/posts/2025-07-14-glassmorphism-test-worst-case-scenario/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-07-14-glassmorphism-test-worst-case-scenario/</guid><pubDate>Mon, 14 Jul 2025 00:00:00 GMT</pubDate><category>accessibility</category><category>css</category><author>Tim Severien</author></item><item><title>The Bear Blog question challenge, mapped</title><link>https://tsev.dev/posts/2025-05-06-bear-blog-challenge-map/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-05-06-bear-blog-challenge-map/</guid><pubDate>Tue, 06 May 2025 00:00:00 GMT</pubDate><category>blog</category><category>random</category><author>Tim Severien</author></item><item><title>The state of the front-end and full-stack job market</title><link>https://tsev.dev/posts/2025-03-26-the-state-of-the-frontend-and-fullstack-job-market/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-03-26-the-state-of-the-frontend-and-fullstack-job-market/</guid><pubDate>Wed, 26 Mar 2025 00:00:00 GMT</pubDate><category>career</category><category>industry</category><author>Tim Severien</author></item><item><title>2024 in review</title><link>https://tsev.dev/posts/2025-01-01-2024-in-review/</link><guid isPermaLink="true">https://tsev.dev/posts/2025-01-01-2024-in-review/</guid><pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This year felt like one where time stood still.&lt;/p&gt;
&lt;h2&gt;Career&lt;/h2&gt;
&lt;p&gt;Early in 2024, I landed a new job, got to work with a lovely team, became familiar with a new tech stack, got new responsibilities, played a significant part in aligning, shaping culture, and leveling up the engineering team, dabbled with data engineering, and worked on some soft skills.&lt;/p&gt;
&lt;p&gt;Nothing too fancy, but I’m still proud of what I’ve accomplished in light of what dominated my year: grief.&lt;/p&gt;
&lt;h2&gt;Loss&lt;/h2&gt;
&lt;p&gt;Last year was full of firsts. First birthdays without you. First Christmas without you. First time seeing your contact details on my phone, knowing no call or message will reach you. For a long time, I felt so fragile that the smallest mishap would make me crumble to the ground. Nothing else mattered. Episode 1 of season 2 of Arcane perfectly portrayed that vibe in &lt;a href=&quot;https://www.youtube.com/watch?v=3aKscy2HkHw&quot;&gt;the funeral scene&lt;/a&gt; (spoilers), except for me, it took the rest of the year for the world to get its colour back.&lt;/p&gt;
&lt;p&gt;Thanks to my loved ones, and the advice in Julia Samuel’s book &lt;a href=&quot;https://www.juliasamuel.co.uk/books/grief-works&quot;&gt;&lt;em&gt;Grief Works: Stories of Life, Death and Surviving&lt;/em&gt;&lt;/a&gt;, I can gaze down the gigantic hole in me and accept its presence. I can surrender myself to the overwhelming sadness prompted by the smallest things. I can forgive myself for not being at my best and needing more time, despite being made acutely aware of the scarcity and finiteness of that resource. Progress — any progress, no matter how small — is enough.&lt;/p&gt;
&lt;h2&gt;Rocky&lt;/h2&gt;
&lt;p&gt;On a brighter note, we gained a family member this year! Internet friends, meet Rocky!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/2024-in-review-Rocky.jpg&quot; alt=&quot;Black cat with yellow eyes intensely looking off-screen&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Previously, we only had one cat, Maui. He likes having someone around, and we felt bad every time we didn’t have time to play or had to leave the house. Maui doesn’t like other cats though, so we weren’t sure whether that was the right approach. We took a bit of a gamble and got a kitten, thinking Maui could help us mold him into a Maui-compatible cat.&lt;/p&gt;
&lt;p&gt;The kitten we found is another Maine Coon that the cattery dubbed Rocky. It took a while, but we eventually decided Rocky was an already fitting name and a subtle reference to Maui: Dwayne “The Rock” Johnson voiced Maui in Moana.&lt;/p&gt;
&lt;p&gt;They did need time to warm up to each other, but it seems they’re quite compatible. Rocky does like to ruffle a little too rough for Maui, and Rocky blatantly ignores hisses and warnings, so sometimes we intervene. Other than that, everything seems to go exceptionally well. They don’t fight. They share food. They even leave scraps for each other when they shouldn’t, because they’re on different diets.&lt;/p&gt;
&lt;p&gt;Maui and Rocky are two goofballs that, after a couple of months, seem inseparable. Hopefully, Rocky will ease on the ruffling as he grows up, since he’s not a year old and already in Maui’s weight class — he’ll outgrow his older friend soon.&lt;/p&gt;
&lt;h2&gt;2025&lt;/h2&gt;
&lt;p&gt;So, what’s next?&lt;/p&gt;
&lt;p&gt;Perhaps some people need concrete goals to stick by, but a general direction works better for me, so my plans for this year are vague directions on specific themes.&lt;/p&gt;
&lt;p&gt;As always, one of them is this blog. The only motivation to blog is the gratification that some people may leave my blog with a new insight or two. I don’t seek fame. I don’t seek visibility to sell freelance services. In fact, being candid here gave an interviewer ammo to ask difficult questions about my burnout that became the deciding factor why I wasn’t hired. Regardless, I am cooking up some ideas where I can combine several interests.&lt;/p&gt;
&lt;p&gt;A big topic I hope to address is insecurity or a lack of confidence. Decisiveness, proactiveness, what and how I communicate, these soft skills and more are greatly influenced by how comfortable I am. I’m done with being paralyzed by my own insecurity. While I have yet to find out how to grow confidence, I plan on starting exposing myself to uncomfortable scenarios, realise I do just fine, and accept that I’m more competent than I think. If you have any pointers on this, &lt;a href=&quot;https://mastodon.social/@timsev&quot;&gt;hit me up on Mastodon&lt;/a&gt; or &lt;a href=&quot;hi@tsev.dev&quot;&gt;send me an e-mail&lt;/a&gt;, because I can use every help I can get.&lt;/p&gt;
&lt;p&gt;These are my themes for now and I’ll gain new insights and adapt accordingly throughout the year. Regardless where I end up, progress is progress, and that’s enough for now.&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>Mighty robust software with the MACH pattern</title><link>https://fresk.digital/en/articles/mighty-robust-software-with-the-mach-pattern</link><guid isPermaLink="true">https://fresk.digital/en/articles/mighty-robust-software-with-the-mach-pattern</guid><pubDate>Thu, 03 Oct 2024 00:00:00 GMT</pubDate><category>architecture</category><author>Tim Severien</author></item><item><title>JavaScript didn’t cause The Great Divide</title><link>https://tsev.dev/posts/2024-08-03-javascript-didnt-cause-the-great-divide/</link><guid isPermaLink="true">https://tsev.dev/posts/2024-08-03-javascript-didnt-cause-the-great-divide/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/the-great-divide/&quot;&gt;The Great Divide&lt;/a&gt; is one of the most iconic web development blog posts. It was well-received as it resonated with many web developers and it has a well-deserved spot on the list of fundamental web dev blog posts on &lt;a href=&quot;https://esif.dev/&quot;&gt;esif.dev&lt;/a&gt;. While I recognise many of the symptoms it described, something always felt off about the essay.&lt;/p&gt;
&lt;p&gt;The article describes an identity crisis in front-end web development and identifies a chasm between two groups of front-end web developers. In Chris’ own words:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On one side, an army of developers whose interests, responsibilities, and skillsets are heavily revolved around JavaScript. [...] On the other, an army of developers whose interests, responsibilities, and skillsets are focused on other areas of the front end, like HTML, CSS, design, interaction, patterns, accessibility, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When it was published in 2019, I wrote a lot of JavaScript. The volume of JavaScript I wrote gradually, but drastically, increased after 2016, when my day-to-day work moved from building websites to building… websites of another kind. Instead of front-ends for the masse, I started to work on web-based software to simplify administration, enforce processes, and present real-time information for decision-making, usually behind an authentication wall — web applications, if you will.&lt;/p&gt;
&lt;p&gt;While the team I was part of preferred developing lean and robust apps, we had to balance that with shipping features and ensuring our stack was ready for long-term ambitions. Sometimes, our .NET and vanilla JavaScript stack did get in our way of developing highly reactive interfaces. At some point, we dabbled with Node.js and Handlebars (and friends) to render templates server-side and client-side, which worked but had its issues. As front-end frameworks evolved and started to support server-side rendering instead of being mere &amp;lt;abbr title=&quot;single-page app&quot;&amp;gt;SPA&amp;lt;/abbr&amp;gt;s, we felt comfortable to adopt them for projects where we anticipated a lot of reactivity.&lt;/p&gt;
&lt;p&gt;With that background in mind, I suppose one of the two buckets Chris described in The Great Divide fits me very well — the JavaScript one — despite caring tremendously about &amp;lt;q&amp;gt;HTML, CSS, design, interaction, patterns, accessibility, etc&amp;lt;/q&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I think Chris, and the people he quotes, accurately identified an identity crisis back then. He puts it eloquently:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The divide is between people who self-identify as a (or have the job title of) front-end developer, yet have divergent skill sets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Later, in the “JavaScript dun got big” section&lt;a href=&quot;https://css-tricks.com/the-great-divide/#post-281105&quot;&gt;^1&lt;/a&gt;, Chris describes the emergence or growth of the JavaScript ecosystem and lists it as a potential cause. Attitude towards JavaScript does seem to be a very distinct difference between people who build public websites and those who do not, but I don’t think that’s the thing that divides us.&lt;/p&gt;
&lt;p&gt;Web technologies broke out of the browser and enables us to develop websites, web apps, desktop applications, mobile applications, and more. On top of that, since JavaScript got non-browser runtimes, it’s become a popular general-purpose programming language, especially for software that orbits around websites and web applications, like server-side HTTP routers and REST APIs. While, historically, HTML, CSS, and JavaScript were synonymous with web development, not all website-esque software are websites nowadays. Moreover, the web isn’t the open and public space it used to be, and some websites serve small audiences in predictable conditions.&lt;/p&gt;
&lt;p&gt;Each of the different things we’re building comes with its own challenges. Lightweight websites may outcompete those that are not. For many web apps, however, shaving off kilobytes won’t do as much as shipping features your users want and your competitors don’t have. Thus, for web apps, shipping often gets prioritized over optimising product rendering performance.&lt;/p&gt;
&lt;p&gt;It’s important to acknowledge that there are some universal truths for all software based on web technologies. Semantic HTML and accessibility should always be up to par. Another principle is that more performant interfaces are always better, and eliminating client-side JavaScript is a reliable way to get there. The Microsoft Edge team is doing exactly that&lt;a href=&quot;https://blogs.windows.com/msedgedev/2024/05/28/an-even-faster-microsoft-edge/&quot;&gt;^2&lt;/a&gt; by ridding themselves of React. Despite that, we mustn’t forget that context matters in decision-making — I’m sure Microsoft Edge and your average &amp;lt;abbr title=&quot;software-as-a-service&quot;&amp;gt;SaaS&amp;lt;/abbr&amp;gt; startup don’t have the same resources and priorities, so where one company can afford to focus on optimizing rendering the performance, another may not.&lt;/p&gt;
&lt;p&gt;As different developers face other challenges, it shapes their perspectives, skills and tangent knowledge. Someone who worked at &amp;lt;abbr title=&quot;business-to-business-to-employee&quot;&amp;gt;B2B2E&amp;lt;/abbr&amp;gt; SaaS startups for years might be unaware of the best practices for websites. Instead, they apply standards and priorities that work for building web apps to something else. That’s how you get developers that don’t care about &lt;code&gt;view-source&lt;/code&gt; — their code usually isn’t public, anyway. That’s how you get a website where client-side React blocks critical content — it’s their favourite hammer and the ramifications are less severe for their day-to-day work.&lt;/p&gt;
&lt;p&gt;The idea that developers either gravitate towards JavaScript or other web technologies is an oversimplification to me. It might be true for some, but I believe that developers are also shaped by the goals they aim to achieve. It seems that the different kinds of software we can build with web tech are different enough to adopt very different beliefs.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;A JavaScript-oriented back-of-the-front-end developer&lt;a href=&quot;https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/&quot;&gt;^3&lt;/a&gt; may have fewer skills in common with a front-of-the-front-end developer than with a &lt;a href=&quot;https://www.djangoproject.com&quot;&gt;Django&lt;/a&gt; or &lt;a href=&quot;https://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; developer. These roles use completely different programming languages, but the choices developers have to make and the skills required to make them are quite similar, indeed. If that doesn’t scream “the role front-end developer was blown into meaningless proportions,” then I don’t know what will. Just like Django developers and AI engineers aren’t referred to as Python developers on the basis that they use the same programming language, I don’t think everyone who writes HTML, CSS, and JavaScript is a front-end developer.&lt;/p&gt;
&lt;p&gt;So, what should we split the title “front-end developer” into?&lt;/p&gt;
&lt;p&gt;To me, front-end developers were and still are primarily (but not exclusively) client-side specialists in web development. Developers who use web technology for things that aren’t websites aren’t web developers. So when people who develop websites are web developers, and people who develop apps for smartphones and tablets are mobile app developers, then developers who build web apps must be… web app developers?&lt;/p&gt;
&lt;p&gt;Perhaps I’m not the best person to propose job titles, but let it be known that I like goal-oriented names because they’re not verbose but explicit enough to understand what implicit knowledge is required. For example, web development often involves at least some knowledge about &amp;lt;abbr title=&quot;search engine optimization&quot;&amp;gt;SEO&amp;lt;/abbr&amp;gt;, because it significantly influences the discoverability of a website. Similarly, mobile app developers probably need to know how apps are launched on app stores, and how to handle back-end communication for users with an older version of the app.&lt;/p&gt;
&lt;p&gt;If a role happens to be platform-agnostic, because you’re using a rendering library that allows you to compile code for multiple platforms, we can think of matching names. Titles like product developer and UI engineer come to mind, though they could be a little more honest — React product developer and React UI engineer are probably more accurate.&lt;/p&gt;
&lt;p&gt;We can split roles up beyond the type of software that developers work on. Brad Frost proposes this in his article, &lt;a href=&quot;https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/&quot;&gt;front-of-the-front-end and back-of-the-front-end web development&lt;/a&gt;. While he addresses web development specifically, I think this equally applies to non-web front-end roles. It’s not catchy, but I can think of some people who fit into a front-of-the-front-end web app developer role. Perhaps this is the level where, instead of trying to capture things in a role, we should be open about our preferences and knowledge so we can try to put complimenting developers together. As Chris puts it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My hope is that the solution is writing more descriptive job postings. If clearly defined and agreed-upon job titles are too much of an ask for the industry at large (and I fear that it is), we can still use our words.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;I believe the essay The Great Divide put too much emphasis on JavaScript being the thing that divided us. Instead, I think the chasm is caused by the diversification of developers using web technologies well beyond the browser. Some of these can be JavaScript-heavy with little to no consequence while doing the same for a public website is bordering on negligence.&lt;/p&gt;
&lt;p&gt;Because the technologies used were historically used for front-end web development, we accidentally rebranded the title to capture many roles that involve writing HTML, CSS, and JavaScript, ignoring how widely applicable web technologies are and how diverse contexts are. Again, some contexts call for JavaScript-averseness, while it’s the bread and butter in others.&lt;/p&gt;
&lt;p&gt;In 2019, the absence of boundaries was confusing. In the communities I’m active in, front-end web developers felt like JavaScript devs were intruding, and their response was often interpreted as gatekeeping. With companies and hiring managers being equally confused, developers who could’ve been complimenting each other were competing for the same job.&lt;/p&gt;
&lt;p&gt;Since the essay was published, I do feel things have improved a bit. There’s now an implicit boundary that slowly becomes explicit — not everyone goes by front-end developer nowadays. Despite that, I regularly run into hostile front-end devs who shame anyone into submission who dares say JavaScript, and developers that objectively think SPA websites are great because everyone has JavaScript enabled and assets cached, so we still have a way to go in differentiating front-end-esque developers and teaching them when to apply which principles.&lt;/p&gt;
&lt;p&gt;I think we can start to co-exist when we abandon views that the out-group is dogmatic, not goal-oriented, or harmful. Everyone is just trying to do the right thing, and sometimes they need a little help shifting perspectives to bring the right toolbox for the job. Sometimes the job requires you to finish early, sometimes it requires precision. As the web platform continues to evolve, and we find more uses for it, that toolbox grows and gets too big for us to master… again, which is why we needed better job titles back in 2019, and need them now.&lt;/p&gt;
&lt;hr /&gt;
</content:encoded><category>js</category><category>opinion</category><author>Tim Severien</author></item><item><title>Procrastination to progress: keeping up the job search</title><link>https://tsev.dev/posts/2023-12-24-keeping-up-the-job-search/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-12-24-keeping-up-the-job-search/</guid><pubDate>Sun, 24 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A couple of months ago, I was laid off. My performance review was positive, and I was set up for promotion, but then a couple of events outside our control happened, leading to necessary cuts.&lt;/p&gt;
&lt;p&gt;For the first time in my career, I found myself unemployed.&lt;/p&gt;
&lt;p&gt;Unplanned unemployment sucks. Who would’ve thought that losing income, a vital resource for most of us, isn’t fun?! The future is uncertain, your confidence is tested, and time and effort sometimes feel wasted pursuing a lead that goes nowhere. When the market is rough, which it currently is, you’re likely applying for one job after another, which can be a cycle of excitement and disappointment.&lt;/p&gt;
&lt;p&gt;It’s challenging to find motivation in these stressful times. Similar to many peers who are in the same situation, I started to show unproductive behaviour like procrastination, distraction seeking, and restlessness, to the point where I stopped doing anything at all. My morale was very low and I showed it.&lt;/p&gt;
&lt;p&gt;Something needed to change.&lt;/p&gt;
&lt;p&gt;Although I do have some control over my morale and motivation, I doubt it’s enough to deal with this. Luckily, I respond well to a daily schedule, and I just happened to be in the rare position where I could schedule time however I liked, so I chose to try a daily routine.&lt;/p&gt;
&lt;h2&gt;A forgiving schedule&lt;/h2&gt;
&lt;p&gt;When going over the next day mentally, I’d tell myself it’s okay to start my day sleeping in. When the pressure and stakes are high, I struggle to cast things off my mind. Consequently, it can take hours to fall asleep, and I wake up early, ultimately causing sleep deprivation. By scheduling and permitting myself to start the day slow, I’d find the peace to fall back asleep, allowing me to start most days rested.&lt;/p&gt;
&lt;p&gt;After my morning routine, I’d search and review job openings for about two hours. I mainly browsed job sites like &lt;a href=&quot;https://www.linkedin.com/&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;https://wellfound.com/&quot;&gt;Wellfound&lt;/a&gt;, &lt;a href=&quot;https://remoteok.com/&quot;&gt;Remote OK&lt;/a&gt;, &lt;a href=&quot;https://pragmatic-engineer.pallet.com/jobs&quot;&gt;The Pragmatic Engineer Job Board&lt;/a&gt;, and more. My friends from the &lt;a href=&quot;https://fronteers.nl/about&quot;&gt;Fronteers&lt;/a&gt; Slack community would also frequently send me great openings — they were very supportive. Anything that looked somewhat interesting would make it to a ranked list.&lt;/p&gt;
&lt;p&gt;Typically after lunch, I’d take an hour or two to work my way down that ranked list and submit applications. Many of these were online forms, sometimes requiring a cover letter, while others required me to set up a profile with redundant information that’s already on my LinkedIn profile and in my CV. Some days I’d easily fill in four forms, while other days I was stuck trying to write a tailored cover letter. By tracking each application in a Notion board, I got some sense of progress and resolution.&lt;/p&gt;
&lt;p&gt;The rest of the day was flexible, and I would play it by ear. With spare energy left, I’d continue working by tweaking my resume, updating profiles, searching for openings and applying to them. On bad days, I’d switch to chores or leisure and get my mind off unemployment for a bit.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;This schedule helped me immensely. As I hoped, it created the routine, rhythm, and momentum to consistently get work done. It also let me recharge and permitted me to seek distractions when needed. It boosted my productivity and made me more optimistic.&lt;/p&gt;
&lt;p&gt;Although this was a success, the takeaway is that it helps to stop, reflect, and leverage self-knowledge. Even though sometimes it feels like motivation drives me, this was a great exercise that proved I can take concrete steps to work around it when it’s lacking.&lt;/p&gt;
</content:encoded><category>career</category><author>Tim Severien</author></item><item><title>Should AVIF be the dominant image format on the web?</title><link>https://tsev.dev/posts/2023-11-10-should-avif-be-the-dominant-image-format/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-11-10-should-avif-be-the-dominant-image-format/</guid><pubDate>Fri, 10 Nov 2023 00:00:00 GMT</pubDate><category>web</category><author>Tim Severien</author></item><item><title>Please add categories to blog feeds</title><link>https://tsev.dev/posts/2023-09-20-add-categories-to-blog-feeds/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-09-20-add-categories-to-blog-feeds/</guid><pubDate>Sun, 17 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I parsed RSS and Atom feeds in various recent projects, and I keep running into the same issue: the absence of categories.&lt;/p&gt;
&lt;p&gt;In one project, I compiled a list of people that communicate about web technologies. The project is not live, but I have mentioned some stats in a &lt;a href=&quot;https://mastodon.social/@timsev/109868477007472835&quot;&gt;two-toot thread&lt;/a&gt; on Mastodon (&lt;em&gt;throot&lt;/em&gt;, anyone?). I want to aggregate these feeds per topic, like a list of Accessibility posts by the likes of Erik Kroes, Hidde de Vries, Scott O&apos;Hara, Sara Soueidan, and more.&lt;/p&gt;
&lt;p&gt;I also created a Slack Workspace in which I want to feed blog posts to. Ideally, I wanted posts to end up in relevant channels. Given the example above, we could have those blog posts end up in the &lt;code&gt;#accessibility&lt;/code&gt; channel.&lt;/p&gt;
&lt;p&gt;A lot of bloggers don’t stick to a single topic. Assigning authors to a specific channel would make my Accessibility blog feed and &lt;code&gt;#accessibility&lt;/code&gt; channel include Hidde’s post &lt;a href=&quot;https://hidde.blog/llm-theft-opt-out/&quot;&gt;It’s pretty rude of OpenAI to make their use of your content opt-out&lt;/a&gt;, which I’d rather see in the &lt;code&gt;#ai&lt;/code&gt; channel. Some stick to a niche, like how &lt;a href=&quot;https://2ality.com&quot;&gt;Dr Axel Rauschmayer’s blog&lt;/a&gt; and my &lt;a href=&quot;https://ijsjes.dev&quot;&gt;ijsjes.dev&lt;/a&gt; are mainly about JavaScript and TypeScript, but those are the exception.&lt;/p&gt;
&lt;p&gt;Finally, as a consumer, I sometimes don’t want to subscribe to everything on a blog. If I could somehow filter posts based on topics, I would. To my knowledge, few feed readers support filtering based on categories, but it would be neat. Perhaps someone built a feed proxy that can.&lt;/p&gt;
&lt;p&gt;At one point, I considered analysing the content. The first problem is that not all feeds contain the body of each post. We can work around that by fetching the content from the entry’s URL, which is where we get to the hard part. How can we tell what content is about? I’ve mentioned accessibility in this post — is this post about accessibility? It turns out, that distilling categories out of content is a non-trivial Natural Language Processing problem. I’m sure someone published an open-source AI model on &lt;a href=&quot;https://huggingface.co&quot;&gt;Hugging Face&lt;/a&gt; that could be of use, though I have doubts it’ll perform well for niche topics.&lt;/p&gt;
&lt;p&gt;It turns out RSS and Atom feeds support categories. See &lt;a href=&quot;https://www.rssboard.org/rss-specification#hrelementsOfLtitemgt&quot;&gt;the &lt;code&gt;category&lt;/code&gt; element in the RSS specification&lt;/a&gt; and &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc4287#section-4.2.2&quot;&gt;the &lt;code&gt;atom:category&lt;/code&gt; element in the Atom specification&lt;/a&gt;. Why not use that, instead?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Atom --&amp;gt;
&amp;lt;entry&amp;gt;
	&amp;lt;title&amp;gt;...&amp;lt;/title&amp;gt;
	&amp;lt;category term=&quot;accessibility&quot; /&amp;gt;
	&amp;lt;category term=&quot;web&quot; /&amp;gt;
	&amp;lt;!-- ... --&amp;gt;
&amp;lt;/entry&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- RSS --&amp;gt;
&amp;lt;item&amp;gt;
	&amp;lt;title&amp;gt;...&amp;lt;/title&amp;gt;
	&amp;lt;category&amp;gt;accessibility&amp;lt;/category&amp;gt;
	&amp;lt;category&amp;gt;web&amp;lt;/category&amp;gt;
	&amp;lt;!-- ... --&amp;gt;
&amp;lt;/item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I glossed over 30 different feeds, and about ⅓ have categories defined. As one might expect, these are non-standardised string values that can be set to whatever the author wants. Consequently, they’re inconsistent: we find variants of the same word, like &lt;em&gt;accessibility&lt;/em&gt; and &lt;em&gt;a11y&lt;/em&gt;, or &lt;em&gt;JavaScript&lt;/em&gt; and &lt;em&gt;JS&lt;/em&gt;. Posts may be tagged with concepts on different levels in a hierarchy, like &lt;em&gt;web&lt;/em&gt; and &lt;em&gt;WebGL&lt;/em&gt;, or &lt;em&gt;CSS&lt;/em&gt; and &lt;em&gt;Flexbox&lt;/em&gt;. Finally, some feeds have very generic categories. One feed I checked has all tech-related posts filed under &lt;em&gt;blog&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Time and time again, I’m surprised by the web development blogging community and the power of feeds. Feed categories is another strength I feel we should tap into more. In web development, we have a well-defined vocabulary of technologies and concepts, allowing us to use semi-consistent tags or categories in our feeds. Although ambitions, filtering (and/or aggregating) based on categories feels feasible.&lt;/p&gt;
</content:encoded><category>blog</category><author>Tim Severien</author></item><item><title>Books books books</title><link>https://tsev.dev/posts/2023-09-08-books/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-09-08-books/</guid><pubDate>Fri, 08 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just before I started working again during &lt;a href=&quot;/posts/2022-07-21-lessons-learned-from-burnout&quot;&gt;my burnout&lt;/a&gt;, I thought stoicism could help me process my thoughts and feelings better, so I asked &lt;em&gt;How to Be Free&lt;/em&gt; for my birthday. It’s a tiny book that lists the teachings of Epictetus, a well-known stoic philosopher from ancient Greece. Despite enjoying it, it took me a while to finish.&lt;/p&gt;
&lt;p&gt;Fast forward a year. I came across a copy of &lt;em&gt;Ik weet je wachtwoord&lt;/em&gt; (translation: I know your password). It’s a Dutch book by journalist Daniël Verlaan about cybersecurity. The author takes you on a ride through data leaks, exploits, cybercrime, the ethics of reporting on and publishing those, interviews with victims and perpetrators, and of course tips on how to protect yourself. I couldn’t get enough and found myself reading every night.&lt;/p&gt;
&lt;p&gt;I’ve virtually never read a book from cover to cover until these. “I don’t have the patience,” I’d say. In hindsight, it wasn’t a lack of patience, but failing to see the benefit of long-form content. For years, I’ve consumed digestible bites of educational content that glosses over a topic in 30 minutes or less and got hands-on when something sparked my interest enough. I never realised I had a blind spot for topics that can’t be learned via this hands-on approach. Short tidbits and long-form content are complementary! It’s embarrassing that took me well over 30 years to realise.&lt;/p&gt;
&lt;p&gt;“What about novels,” I hear you asking. My focus continues to be exploring ideas to learn from, but I have a handful of novels in my reading queue.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;/books&quot;&gt;the books page&lt;/a&gt; if you’re interested in what I’m reading, and please &lt;a href=&quot;https://mastodon.social/@timsev&quot;&gt;recommend me a book on Mastodon&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>Guiding developers through a codebase with inline guides</title><link>https://tsev.dev/posts/2023-08-29-guiding-engineers-with-inline-guides/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-08-29-guiding-engineers-with-inline-guides/</guid><pubDate>Tue, 29 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes, adding something to a codebase is routine. One that is so well-defined, that someone can point you to the exact files and lines in a codebase where changes are necessary.&lt;/p&gt;
&lt;p&gt;Instead of telling people what to do where, at &lt;a href=&quot;https://www.dadohr.com&quot;&gt;Dado&lt;/a&gt;, we wanted people to be able to discover and follow these steps autonomously.&lt;/p&gt;
&lt;h2&gt;Tracking lines of code externally&lt;/h2&gt;
&lt;p&gt;When I say documentation, a lot of people will think of tools like Confluence or Slab. Unfortunately, it’s incredibly hard to keep external references to a codebase synchronized, simply because code is so volatile. We may get away with documenting slow-moving aspects, like system design, architecture, principles, and patterns, but specific files and lines of code may change daily.&lt;/p&gt;
&lt;p&gt;We’ve tried to use &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks&quot;&gt;Bookmarks&lt;/a&gt; to mark points of interest. This extension lets you mark specific lines in files, tracks these as you make changes, and saves them in a file in your repository. This may work well on a project you’re working on alone, but less so in a team. Sadly, the file containing the bookmarks frequently had to be updated manually after a rebase or merge. After forgetting that a couple of times, the bookmarks no longer referred to the code we wanted to track.&lt;/p&gt;
&lt;p&gt;We concluded that if we want to document these steps, we can’t do that externally — it has to be in the code. This way, the reference sticks to where it’s supposed to be unless deliberately moved.&lt;/p&gt;
&lt;h2&gt;Inline guides&lt;/h2&gt;
&lt;p&gt;Similar to how JSDoc annotates functions and whatnot, we came up with a comment format to annotate points of interest for certain tasks. It’s inline, right next to the relevant code.&lt;/p&gt;
&lt;p&gt;Let’s say we’re working on an app where users can supply information by uploading a file with tabular data (e.g. CSV, Excel, Apache Arrow, etc.), and we want to write a guide for adding a new file format, we could add these comments to make a multistep guide:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// @guide Add import file - 1.0 Register file format

// @guide Add import file - 1.1 Add file format parser

// @guide Add import file - 2.0 Add format on integrations marketing page
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each guide has a name which serves as a guide identifier. The step number is used for ordering. That might not be necessary as you’ll probably complete all steps before shipping, but can be useful to build a mental model. Lastly, there’s a step description to describe what action is required for this step.&lt;/p&gt;
&lt;h3&gt;Where to find them&lt;/h3&gt;
&lt;p&gt;It’s cool we have these guides, but we can’t expect users to scan all files in a codebase until they find them, can we? Initially, we started doing a full-text searches for &lt;code&gt;@guide&lt;/code&gt; to find the guide we wanted to follow, and then searched for &lt;code&gt;@guide [name]&lt;/code&gt; to find its steps. Sadly, the list of results is ungrouped and unsorted, so we set out to create a better list.&lt;/p&gt;
&lt;p&gt;We first searched these with grep, a Unix command-line utility. The following command will scan for &lt;code&gt;@guide&lt;/code&gt; recursively, starting from the current working directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep &quot;@guide&quot; . --exclude-dir=node_modules --ignore-case --line-number --recursive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I expected it to be slow, but it’s surprisingly fast! Using the command above, a codebase of 500,000+ lines of code gets scanned in about half a second. You can then parse the output and sort the results.&lt;/p&gt;
&lt;p&gt;Despite being surprisingly fast, a faster alternative is using &lt;a href=&quot;https://github.com/BurntSushi/ripgrep&quot;&gt;ripgrep&lt;/a&gt;. Ripgrep is a command-line utility that’s similar to grep, except that it’s cross-platform and also takes your &lt;code&gt;.gitignore&lt;/code&gt; into account, so it automatically skips over ignored files. The following command is pretty much equivalent to the grep command from before:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rg &quot;@guide&quot; . --ignore-case --hidden
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We realised we could list the guides with a Visual Studio Code extension, and built the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=DadoHR.inline-guides&quot;&gt;Inline guides extension&lt;/a&gt;. Instead of fiddling in the console, it adds a pane listing all guides and lets you navigate to these lines with a single click. Note that the plugin isn’t polished — we built it to make it work for us.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/inline-guides-vscode.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;How is it?&lt;/h3&gt;
&lt;p&gt;Inline guides have proved to be a great tool to make sure we don’t miss a step, even when we find ourselves in unfamiliar places. It lets the team members dive into deep waters littered with floaties for them to latch on to. It also served as a checklist for developers who know exactly where they need to be, making it was useful for the entire team, regardless of their level or familiarity with the codebase.&lt;/p&gt;
&lt;h3&gt;Limitations&lt;/h3&gt;
&lt;p&gt;Some files that could have a guide in them, might also be included in the &lt;code&gt;.gitignore&lt;/code&gt; file, like configuration files that contain secrets. Our use of ripgrep means &lt;code&gt;@guide&lt;/code&gt;s in these files aren’t included in the list. That can be a bit of a bummer, though ripgrep can be modified to use a custom ignore list, instead.&lt;/p&gt;
&lt;p&gt;Another thing is that you can’t add &lt;code&gt;@guide&lt;/code&gt; comments to files that don’t exist. If a step requires adding a new file, we can add a guide to potentially relevant import statements, which don’t exist if the file is loaded dynamically.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;This approach of directing people through a codebase works amazingly well, especially when autonomy is valued. The applications are limited, though. There’s little use of describing guides for tasks that aren’t repeated. At best, one could use them to create some sort of onboarding tour.&lt;/p&gt;
&lt;p&gt;If you do have a codebase where a part of it is extensible, and you’re levering that extensibility frequently to add in new features, inline guides are amazing.&lt;/p&gt;
</content:encoded><category>productivity</category><category>tools</category><author>Tim Severien</author></item><item><title>Setting up a modern JavaScript monorepo</title><link>https://tsev.dev/posts/2023-08-27-setting-up-a-modern-monorepo/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-08-27-setting-up-a-modern-monorepo/</guid><pubDate>Sun, 27 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you smell that? Ahh, it’s the scent of a greenfield project’s potential. The aroma of not making old mistakes again. The opportunity to do it better this time. It’s time to set up a monorepo!&lt;/p&gt;
&lt;p&gt;Recently, I’ve been experimenting with various ways of setting up monorepos for side projects. Whereas I’d previously shy away from the additional complexity of stringing packages together, modern tools make it a much easier and more pleasant experience. Let’s find out what it’s like today!&lt;/p&gt;
&lt;p&gt;For this article, I’ve prepared a &lt;a href=&quot;https://github.com/timseverien/tsev-monorepo&quot;&gt;repository&lt;/a&gt; with the result. If you want, you can check out the &lt;a href=&quot;https://github.com/timseverien/tsev-monorepo/commits/main&quot;&gt;commit history&lt;/a&gt; to follow along – most commits correspond to a step in this post.&lt;/p&gt;
&lt;h2&gt;What’s a monorepo again?&lt;/h2&gt;
&lt;p&gt;A monorepo is a repository that typically holds several applications, packages, or modules. Having these together in a single repository helps to keep these synchronized.&lt;/p&gt;
&lt;p&gt;Let’s say you’re publishing several npm packages that are related to each other, like a core package and some plugins. Or maybe you’re building a web application that communicates with a REST API. In both cases, you’ll want to keep these parts in sync. The easier that is, the better. Another benefit is that it helps share code between applications — no need to pull &lt;a href=&quot;https://git-scm.com/docs/gitsubmodules&quot;&gt;submodules&lt;/a&gt; or deploy packages to npm repositories.&lt;/p&gt;
&lt;p&gt;The trade-off? We need additional tooling and configuration to make it all work.&lt;/p&gt;
&lt;h2&gt;Pick your poison&lt;/h2&gt;
&lt;p&gt;You might’ve heard of &lt;a href=&quot;https://lerna.js.org&quot;&gt;Lerna&lt;/a&gt;. The tool used to be immensely popular to set up monorepos, until it wasn’t. The project became inactive from early 2021 to early 2022, but it’s back again, this time powered by &lt;a href=&quot;https://nx.dev&quot;&gt;Nx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;During its inactivity, and perhaps deterred by the commercial aspect of Nx, some have switched from Lerna to &lt;a href=&quot;https://yarnpkg.com&quot;&gt;Yarn&lt;/a&gt; or &lt;a href=&quot;https://pnpm.io&quot;&gt;pnpm&lt;/a&gt; workspaces. Both of these are alternative package managers that generally have an edge over npm, node.js’ default package manager, while still leeching off npm’s package repository.&lt;/p&gt;
&lt;p&gt;Although npm typically lags behind its alternatives, it did catch up a little bit with &lt;a href=&quot;https://docs.npmjs.com/cli/using-npm/workspaces&quot;&gt;npm workspaces&lt;/a&gt;. It doesn’t have all the fancy stuff the other tools have, but npm has all the features I need for my uses — maybe for you, too!&lt;/p&gt;
&lt;p&gt;If you want advice: take a moment to compare Yarn, pnpm, and npm. Browse their documentation, run some tests, and discover which fits your project best. If they’re all good enough, choose whatever you or your team is most familiar with.&lt;/p&gt;
&lt;p&gt;I’ve had relatively little trouble with npm, and as long as node.js is the reigning JavaScript runtime, I suspect it will outlast current and future alternatives. Since the tool works well enough for monorepos, I’ll use npm in the rest of this article. Regardless of which tool you choose, I’m sure you can follow along.&lt;/p&gt;
&lt;h2&gt;Setting up the workspace&lt;/h2&gt;
&lt;p&gt;Let’s say we’re building a multilingual web application. We already know that, at some point, we’ll have another user-facing app with the same copy. It’d be useful to have a monorepo with a separate internationalization package, ready to use in applications we’ll build in the future.&lt;/p&gt;
&lt;p&gt;We start by creating a &lt;code&gt;package.json&lt;/code&gt; in our root directory. While we’re there, let’s also define our workspaces by adding a &lt;code&gt;workspace&lt;/code&gt; property with a list of paths:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// package.json
{
	&quot;private&quot;: true,
	&quot;workspaces&quot;: [&quot;packages/app&quot;, &quot;packages/i18n&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To initialize each workspace, we can run &lt;code&gt;npm init --workspace ./packages/{name}&lt;/code&gt;. This command creates the directories and initializes a &lt;code&gt;package.json&lt;/code&gt;. If you prefer, you can do that manually, too.&lt;/p&gt;
&lt;p&gt;Note that the &lt;code&gt;name&lt;/code&gt; field in these &lt;code&gt;package.json&lt;/code&gt; files is used when importing files, so it’s recommended to prefix them to avoid clashing with actual npm packages. The name doesn’t have to match the directory name, so you can be creative!&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/app/package.json
{
	&quot;name&quot;: &quot;@mono/app&quot;,
	&quot;type&quot;: &quot;module&quot;,
	&quot;private&quot;: true,
	&quot;main&quot;: &quot;./index.js&quot;
}

// packages/i18n/package.json
{
	&quot;name&quot;: &quot;@mono/i18n&quot;,
	&quot;type&quot;: &quot;module&quot;,
	&quot;private&quot;: true,
	&quot;main&quot;: &quot;./index.js&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s add a constant to &lt;code&gt;@mono/i18n&lt;/code&gt; and see if we can import that in &lt;code&gt;@mono/app&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/i18n/index.js
export const SUPPORTED_LANGUAGES = [&apos;en-GB&apos;, &apos;nl-NL&apos;];

// packages/app/index.js
import { SUPPORTED_LANGUAGES } from &apos;@mono/i18n&apos;;
console.log(SUPPORTED_LANGUAGES);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we run &lt;code&gt;node packages/app/index.js&lt;/code&gt;, we’ll get an error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error [ERR_MODULE_NOT_FOUND]: Cannot find package &apos;@mono/i18n&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although our root &lt;code&gt;package.json&lt;/code&gt; file is aware of where the packages are, they’re not installed, and the imports won’t resolve. Whenever you add or remove a package, you must run &lt;code&gt;npm install&lt;/code&gt;. This will add symlinks in your &lt;code&gt;node_modules&lt;/code&gt; directory that point to your packages’ source code.&lt;/p&gt;
&lt;p&gt;After running &lt;code&gt;npm install&lt;/code&gt;, the command &lt;code&gt;node packages/app/index.js&lt;/code&gt; should now print out the &lt;code&gt;SUPPORTED_LANGUAGES&lt;/code&gt; variable!&lt;/p&gt;
&lt;p&gt;Many of the npm commands work the same for workspaces, though you will need to add the &lt;code&gt;--workspace=[pkgname]&lt;/code&gt; argument to run a command for a specific workspace. For example, to run the &lt;code&gt;build&lt;/code&gt; script of the &lt;code&gt;@mono/app&lt;/code&gt; package, we’d run &lt;code&gt;npm run build --workspace=@mono/app&lt;/code&gt;. If you want to run a script for all packages, you can run &lt;code&gt;npm run build --workspaces&lt;/code&gt;. You may want to add the &lt;code&gt;--if-present&lt;/code&gt; option to avoid errors if some of your packages don’t have a &lt;code&gt;build&lt;/code&gt; script defined. To learn more about workspaces, read &lt;a href=&quot;https://docs.npmjs.com/cli/using-npm/workspaces&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Adding TypeScript&lt;/h2&gt;
&lt;p&gt;To add TypeScript, we’ll have to modify some configuration; we must make sure the TypeScript compiler plays nicely with these npm workspaces.&lt;/p&gt;
&lt;p&gt;First, we rename our &lt;code&gt;index.js&lt;/code&gt; files from before to &lt;code&gt;index.ts&lt;/code&gt;. Let’s also stick to conventions and move them to a &lt;code&gt;src&lt;/code&gt; subdirectory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/i18n/src/index.ts
export const SUPPORTED_LANGUAGES = [&apos;en-GB&apos;, &apos;nl-NL&apos;];

// packages/app/src/index.ts
import { SUPPORTED_LANGUAGES } from &apos;@mono/i18n&apos;;
console.log(SUPPORTED_LANGUAGES);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let’s set up TypeScript. We can add a common configuration in the root directory that we’ll extend in the &lt;code&gt;tsconfig.json&lt;/code&gt; files of each package:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tsconfig.common.json
{
	&quot;compilerOptions&quot;: {
		&quot;module&quot;: &quot;NodeNext&quot;,
		&quot;moduleResolution&quot;: &quot;NodeNext&quot;,
		&quot;skipLibCheck&quot;: true
	}
}

// packages/i18n/tsconfig.json
// packages/app/tsconfig.json
{
	&quot;extends&quot;: &quot;../../tsconfig.common.json&quot;,
	&quot;include&quot;: [&quot;src/*&quot;],
	&quot;compilerOptions&quot;: {
		&quot;outDir&quot;: &quot;dist&quot;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s add build scripts to build our packages, and update the &lt;code&gt;main&lt;/code&gt; field to refer to the built file. Note that I’ve commented out some parts of our configuration for brevity.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/app/package.json
{
	&quot;name&quot;: &quot;@mono/app&quot;,
	// …
	&quot;scripts&quot;: {
		&quot;build&quot;: &quot;tsc&quot;
	}
}

// packages/i18n/package.json
{
	&quot;name&quot;: &quot;@mono/i18n&quot;,
	// …
	&quot;scripts&quot;: {
		&quot;build&quot;: &quot;tsc&quot;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re almost there! Let’s add another build script to our root &lt;code&gt;package.json&lt;/code&gt; to fire all of these in one go:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// package.json
{
	// …
	&quot;scripts&quot;: {
		&quot;build&quot;: &quot;npm run build --workspaces --if-present&quot;
	}
	// …
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, install TypeScript with &lt;code&gt;npm install typescript&lt;/code&gt;. Because we’re not using the &lt;code&gt;--workspace&lt;/code&gt; argument, TypeScript gets installed and saved in the project root, allowing us to access TypeScript in every package.&lt;/p&gt;
&lt;p&gt;We can now build our project with &lt;code&gt;npm run build&lt;/code&gt;. Success! If we run &lt;code&gt;node packages/app/dist/index.js&lt;/code&gt;, we’ll see that the value of &lt;code&gt;SUPPORTED_LANGUAGES&lt;/code&gt; is printed. It works!&lt;/p&gt;
&lt;p&gt;If we open the project in Visual Studio Code and open &lt;code&gt;packages/app/src/index.ts&lt;/code&gt;, the import statement has a squiggly underline, indicating a warning:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Could not find a declaration file for module &apos;@mono/i18n&apos;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because the &lt;code&gt;main&lt;/code&gt; field in the &lt;code&gt;package.json&lt;/code&gt; of &lt;code&gt;@mono/i18n&lt;/code&gt; refers to a JavaScript file. One that doesn’t have a declaration file. We can get a declaration file by updating &lt;code&gt;packages/i18n/tsconfig.json&lt;/code&gt; and running building that project. This means that to get the correct types in our editor, we’ll need to rebuild &lt;code&gt;@mono/i18n&lt;/code&gt; after every change. That’s a bit inconvenient!&lt;/p&gt;
&lt;p&gt;This happens because TypeScript preserves imports during compilation. In our compiled JavaScript file, it still imports &lt;code&gt;@mono/i18n&lt;/code&gt; and uses its &lt;code&gt;package.json&lt;/code&gt; to figure out which file to read, which points us to the compiled JavaScript file — as it should. If we change it to point to the TypeScript files, node.js will throw an error because it doesn’t understand TypeScript files.&lt;/p&gt;
&lt;p&gt;Luckily, we can make this process much easier. Instead of a compiler, we can use a bundler, like webpack, Rollup, and Vite. This will produce a bundle; a big file that includes dependencies, too. Realistically, we’re likely to do that anyway, since we’re building a web application.&lt;/p&gt;
&lt;h2&gt;Adding Vite&lt;/h2&gt;
&lt;p&gt;For this project, I chose Vite. We’re not going to touch Vite configuration, so I’m sure you can follow along regardless of whether you’re choosing Vite, Astro, Next.js, SvelteKit, Nuxt, or similar tools.&lt;/p&gt;
&lt;p&gt;We’re going to avoid installing Vite with &lt;code&gt;npm create vite&lt;/code&gt;, because we already have a directory with files we want to keep. Instead, we’ll do that manually.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;npm install vite --workspace=@mono/app&lt;/code&gt; to add Vite as a dependency. We can then update our &lt;code&gt;@mono/app&lt;/code&gt; scripts to use Vite:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/app/package.json
{
	&quot;name&quot;: &quot;@mono/app&quot;,
	// …
	&quot;scripts&quot;: {
		&quot;dev&quot;: &quot;vite&quot;,
		&quot;build&quot;: &quot;tsc &amp;amp;&amp;amp; vite build&quot;
	}
	// …
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To let TypeScript know importing works subtly differently now, let’s set the module resolution to &lt;code&gt;bundler&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tsconfig.common.json
{
	// …
	&quot;compilerOptions&quot;: {
		&quot;module&quot;: &quot;ESNext&quot;,
		&quot;moduleResolution&quot;: &quot;Bundler&quot;
		// …
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we can add a basic HTML file to the web app, which serves as an entry point for Vite:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
		&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
		&amp;lt;title&amp;gt;Hello world&amp;lt;/title&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;script type=&quot;module&quot; src=&quot;/src/index.ts&quot;&amp;gt;&amp;lt;/script&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since our bundler will build and bundle &lt;code&gt;@mono/app&lt;/code&gt; and its dependencies in one fell swoop, we no longer need to build &lt;code&gt;@mono/i18n&lt;/code&gt; prior. Let’s update our root &lt;code&gt;package.json&lt;/code&gt; to only run the build script in &lt;code&gt;@mono/app&lt;/code&gt;. While we’re at it, let’s also add a &lt;code&gt;dev&lt;/code&gt; script to start the Vite server:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// package.json
{
	// …
	&quot;scripts&quot;: {
		&quot;dev&quot;: &quot;npm run dev --workspace=@mono/app&quot;,
		&quot;build&quot;: &quot;npm run build --workspace=@mono/app&quot;
	}
	// …
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let’s update &lt;code&gt;@mono/i18n&lt;/code&gt;’s &lt;code&gt;package.json&lt;/code&gt; to point to the TypeScript files again:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/i18n/package.json
{
	&quot;name&quot;: &quot;@mono/i18n&quot;,
	// …
	&quot;main&quot;: &quot;src/index.ts&quot;
	// …
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if we run &lt;code&gt;npm run dev&lt;/code&gt; and open the webpage, the console will show the value of &lt;code&gt;SUPPORTED_LANGUAGES&lt;/code&gt; again!&lt;/p&gt;
&lt;h2&gt;Importing other files&lt;/h2&gt;
&lt;p&gt;All this time we’ve been importing the main file of our packages. We started out importing &lt;code&gt;index.js&lt;/code&gt; and later moved to &lt;code&gt;src/index.ts&lt;/code&gt;. We relied on the &lt;code&gt;main&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; to tell our tools where to find the file we wanted to import.&lt;/p&gt;
&lt;p&gt;Let’s say we have more files in our &lt;code&gt;@mono/i18n&lt;/code&gt; package. How do we import these non-main files?&lt;/p&gt;
&lt;p&gt;Many node modules let you import various files. For example, you can import specific functions from lodash:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import get from &apos;lodash/get&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because we’ve moved our TypeScript files to the &lt;code&gt;src&lt;/code&gt; directory, we have to include it in our path. Yuck!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import t from &apos;@mono/i18n/src/languages/nl-NL&apos;;
console.log(t(&apos;navigationAbout&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can avoid import paths completely and re-export what we want to expose in &lt;code&gt;packages/i18n/src/index.ts&lt;/code&gt;. This can be done with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#re-exporting_aggregating&quot;&gt;&lt;code&gt;export … from …&lt;/code&gt; syntax&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/i18n/src/index.ts
export { default as nl } from &apos;./language/nl-NL&apos;;

// packages/app/src/index.ts
import { nl as t } from &apos;@mono/i18n&apos;;
console.log(t(&apos;navigationAbout&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another way is by specifying export file mapping in our &lt;code&gt;package.json&lt;/code&gt;, where we can map an import path to an actual file in our package. This also allows us to control what we export, but on a file basis instead of individual exports. Consider the following example:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- prettier-ignore --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// packages/i18n/package.json
{
	&quot;name&quot;: &quot;@mono/i18n&quot;,
	// …
	&quot;exports&quot;: {
		&quot;./en-GB&quot;: &quot;./src/languages/en-GB.ts&quot;,
		&quot;./nl-NL&quot;: &quot;./src/languages/nl-NL.ts&quot;
	}
	// …
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the above configuration, we can import language files as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import t from &apos;@mono/i18n/nl-NL&apos;;
console.log(t(&apos;navigationAbout&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To summarize, these are our options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move files back to the root directory, removing the &lt;code&gt;src&lt;/code&gt; directory from the import path.&lt;/li&gt;
&lt;li&gt;Re-export in the &lt;code&gt;src/index.ts&lt;/code&gt;. This gives us control over what code we want to expose and removes paths from import statements.&lt;/li&gt;
&lt;li&gt;Add an export mapping to &lt;code&gt;package.json&lt;/code&gt;. This gives us control over what files we want to expose and lets us define virtual paths to real files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“What about TypeScript aliases with its &lt;code&gt;paths&lt;/code&gt; option?” That’s a good question! Although the previous examples mainly use TypeScript code, I can think of situations where its compiler doesn’t apply, like when we want to import CSS from another CSS file. Most CSS preprocessors support node-esque module resolution, allowing you to import node packages from CSS files, and make use of the options we’ve explored. If that’s no concern you have, feel free to configure TypeScript’s &lt;code&gt;paths&lt;/code&gt; option instead. If it works, it works! Speaking of which, we should test one last time.&lt;/p&gt;
&lt;p&gt;Does &lt;code&gt;npm run build&lt;/code&gt; runs without errors? Check!&lt;/p&gt;
&lt;p&gt;If we run &lt;code&gt;npm run dev&lt;/code&gt; and check out the web page, is the translated string printed in the console? Yep! That means everything works.&lt;/p&gt;
&lt;h2&gt;Now write some code&lt;/h2&gt;
&lt;p&gt;Phew, that was a lot of work, but now it’s finally time to code. If you had trouble following along: don’t fret! You can check out &lt;a href=&quot;https://github.com/timseverien/tsev-monorepo&quot;&gt;the repository&lt;/a&gt;. It contains everything we’ve covered.&lt;/p&gt;
&lt;p&gt;Let me share some closing thoughts: don’t get too hung up about making each package a standalone app or whatever. You know your project best, so you know best what’s worth splitting up. If you’re making a package to mentally separate some code, like separating your application layers, that’s also fine.&lt;/p&gt;
</content:encoded><category>js</category><category>tools</category><author>Tim Severien</author></item><item><title>The 2023 rebuild</title><link>https://tsev.dev/posts/2023-08-18-the-2023-rebuild/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-08-18-the-2023-rebuild/</guid><pubDate>Fri, 18 Aug 2023 00:00:00 GMT</pubDate><category>blog</category><author>Tim Severien</author></item><item><title>Getting better at newsletters, maybe</title><link>https://tsev.dev/posts/2023-02-21-getting-better-at-newsletters/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-02-21-getting-better-at-newsletters/</guid><pubDate>Tue, 21 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday, Chris Coyier shared his system to consume newsletters in his blog post: &lt;a href=&quot;https://chriscoyier.net/2023/02/20/how-to-newsletters/&quot;&gt;How To Newsletters&lt;/a&gt;. This reminded me that my inbox is littered with them. All of which I want to read, but can’t find the mode for. Similarly to what Chris describes, it feels a little too much like a work-related chore to me. Unlike Chris, I don’t use Feedbin, and want to try an idea I’ve been toying with for years.&lt;/p&gt;
&lt;p&gt;Many of my newsletters are, indeed, work-related — lists of web development and software engineering opinion pieces, updates on features and specs, and showcases. They keep me up-to-date, help me shape principles on new and emerging technology, and generally help me grow as a web developer and software engineer. This is one part passion, but I also need them to stay relevant. It’s how I make a living.&lt;/p&gt;
&lt;p&gt;Although I don’t shy away from having work bleed into my personal time, I require intrinsic motivation. I will experiment, test out ideas, and use new frameworks and libraries when starting yet another side-project, but I can’t find the motivation to allocate time to read a handful of articles. I’d rather spend that time with family and friends or pursue hobbies.&lt;/p&gt;
&lt;p&gt;A while ago, I asked a mentee whether they studied or worked on side projects in their spare time, causing them to guilt-trip — they said they were slacking. Although developing my skills in my spare time created opportunities I might’ve missed otherwise, I encourage people to grow their skills at work. Our workplace should facilitate growth.&lt;/p&gt;
&lt;p&gt;With that philosophy in mind, perhaps I can read newsletters at work. To avoid getting distracted by the hundreds of unread emails, I want to avoid opening my personal inbox. I suppose I can resubscribe to newsletters with my work address, but forwarding them lets me “own” my subscriptions and take them with me.&lt;/p&gt;
&lt;p&gt;With my personal Gmail address, I can filter messages by sender, and forward these.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/how-to-newsletter-create-filter.png&quot; alt=&quot;A screenshot of the create filter dialog in Gmail&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After setting this up, I realized I can achieve the same by creating a new email address exclusively for newsletters. That’s probably safer — if the filter for forwarding becomes too forgiving, I might forward (and archive) personal emails.&lt;/p&gt;
&lt;p&gt;Oh well, let’s find out if this gets me to read my subscriptions, first.&lt;/p&gt;
</content:encoded><category>personal</category><category>productivity</category><author>Tim Severien</author></item><item><title>That one framework use-case</title><link>https://tsev.dev/posts/2023-02-01-that-one-framework-use-case/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-02-01-that-one-framework-use-case/</guid><pubDate>Wed, 01 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A &lt;a href=&quot;https://twitter.com/rauchg/status/1619492334961569792&quot;&gt;tweet from Next.js author Guillermo Rauch&lt;/a&gt; sparked uproar on social media, filling my Mastodon timeline quickly with &lt;s&gt;subtweets&lt;/s&gt; subtoots. Often with a bitter and cynic tone, people criticize just about anything remotely related to frameworks.&lt;/p&gt;
&lt;p&gt;Between the sarcastic and toxic comments, there’s valid critique — critique people have repeated for the last 5+ years. Andy Bell’s &lt;a href=&quot;https://andy-bell.co.uk/speed-for-who/&quot;&gt;Speed for who?&lt;/a&gt; is a great post summarizing some of the criticism. I wholeheartedly agree with all of it… when developing certain types of websites.&lt;/p&gt;
&lt;p&gt;Although the vast majority of the web &lt;em&gt;is&lt;/em&gt; public and &lt;em&gt;should&lt;/em&gt; work well on a low-end device on a bad and expensive connection at the most remote place imaginable, it doesn’t mean all websites need to follow that principle.&lt;/p&gt;
&lt;h2&gt;Frameworks can be good&lt;/h2&gt;
&lt;p&gt;There’s a very small subset of websites where data-driven reactivity is desired for usability and &amp;lt;abbr title=&quot;User Experience&quot;&amp;gt;UX&amp;lt;/abbr&amp;gt;, where outsourcing that complexity to a tool can save a lot of time, where users are always on a stable connection and on a high-end device, and thus care more about new and improved features than a (for them) negligible performance increase. For example, I mainly work on highly interactive &amp;lt;abbr title=&quot;business-to-business&quot;&amp;gt;B2B&amp;lt;/abbr&amp;gt; products for employees that have a similar or better device and connection than I have. That’s the only use-case.&lt;/p&gt;
&lt;p&gt;Although frameworks and Single-Page Applications are synonyms historically, that’s no longer the case. Many frameworks offer server-side rendering and partial hydration, which is an absolute minimum for me. Browsers still load a ton of JavaScript, but at least the content is already there when (and if) that happens.&lt;/p&gt;
&lt;p&gt;Like all other tech, it’ll progressively get better. But unlike most tech, frameworks haven’t had decades to mature. Perhaps things aren’t progressing as fast as we’d like, but I’m sure we’ll arrive at a point where they’re mature enough that wide adoption starts to make sense.&lt;/p&gt;
&lt;p&gt;Again, for the vast majority of projects, I too aim for a lightweight static or server-side rendered stack for the exact reasons Andy Bell mentions in his post.&lt;/p&gt;
&lt;h2&gt;Blaming the hammer for hitting a screw&lt;/h2&gt;
&lt;p&gt;Despite all the things that are wrong with frameworks, I don’t think they are inherently bad. Using a tool for the wrong job, is.&lt;/p&gt;
&lt;p&gt;Instead of taking it out on anyone who dare say “framework”, I want educators, mentors, and communicators to do better. As a community, we have failed to teach developers to prioritize users, to inform them that technology impacts user experience, and that we need to be thoughtful about when to use what.&lt;/p&gt;
&lt;h2&gt;Denormalize toxicity&lt;/h2&gt;
&lt;p&gt;A significant influx of web developers have no idea when to use frameworks. They’re only taught the benefits and how to use them, and enter the industry relatively JavaScript-savvy.&lt;/p&gt;
&lt;p&gt;Now imagine they turn to social media to follow prominent developers. Some of them share intriguing thoughts. Others have interesting discussions with peers. And then there’s a prominent figure that groups the JavaScript community with fascists. Or that CSS framework author that aggressively defends it when someone shares legitimate criticism. Welcome to the web development community, where our dogmas render us incapable to partake in civilized discourse!&lt;/p&gt;
&lt;p&gt;It’s ironic how one of our main principles is to build inclusive websites, while actively making the community obnoxious and unwelcoming. Let’s practice what we preach and get back to having constructive conversations again.&lt;/p&gt;
</content:encoded><category>industry</category><category>tools</category><category>web</category><author>Tim Severien</author></item><item><title>Is TypeScript worth it?</title><link>https://tsev.dev/posts/2023-01-25-is-typescript-worth-it/</link><guid isPermaLink="true">https://tsev.dev/posts/2023-01-25-is-typescript-worth-it/</guid><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just read Chris Coyier’s &lt;a href=&quot;https://chriscoyier.net/2023/01/24/you-like-it-because-you-know-it/&quot;&gt;You Like It Because You Know It&lt;/a&gt;, in which he shares some thoughts about TypeScript and wonders whether it’s worth it.&lt;/p&gt;
&lt;p&gt;I’m in a similar position: I’ve been writing TypeScript professionally for over six months, and used it in hobby projects on and off for a year or two. Unlike Chris, I’m definitely on team TypeScript now (but I won’t be obnoxious about it, I promise).&lt;/p&gt;
&lt;h2&gt;The wrong type of bug&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I have lingering doubts about how many bugs are actually about types. Of all the JavaScript bugs I’ve fixed in my years, it honestly doesn’t feel like that many were about wrongly typed data being flung around. Some days it feels like a wild hoax that an entire language was invented around solving these not particularly common bugs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It took me years to thoroughly understand the dangers of JavaScript’s dynamic typing and learning how to mitigate potential bugs. I think, like myself, many JavaScript programmers learned to use the strict equality operator (&lt;code&gt;===&lt;/code&gt;) and obsessively validate values.&lt;/p&gt;
&lt;p&gt;With TypeScript, we still do. It only helps us avoid bugs by forcing us to be explicit about types, or rather type annotations. TypeScript only transpiles code and detects mismatching annotations. It’s a facade, smoke and mirrors, a &lt;a href=&quot;https://jsdoc.app&quot;&gt;JSDoc&lt;/a&gt; on steroids.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const n = 4.2 as any as string;
console.log(n.substring(0, 1));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code will transpile without a problem. When executed, however, JavaScript will throw a type-related error. Because we have to jump syntactical hoops to use a number as a string, we become aware of the issue before it happens — TypeScript prevents type bugs through us. Instead of relying on our own discipline to mangle data responsively, we can shift some of that responsibility to TypeScript.&lt;/p&gt;
&lt;h2&gt;Data you can trust&lt;/h2&gt;
&lt;p&gt;Now that we’ve established TypeScript doesn’t actually do much other than make types visible, let’s explore more indirect effects.&lt;/p&gt;
&lt;p&gt;As I learned to mitigate type errors in JavaScript, I started by validating parameters for every function. This adds a lot of noise and repetition. Over time, a better practice emerged: validating data at application boundaries and preserving data integrity within. An example of this is validating user input and sanitizing it to a meaningful format before the data is passed to other code. TypeScript nudges developers in that direction. As we try to increase certainty and replace the &lt;code&gt;any&lt;/code&gt; type with meaningful types, validation and type assertions automatically move towards the boundaries of the application.&lt;/p&gt;
&lt;p&gt;In this vacuum where we can (to some degree) trust that data has a specific type and format, TypeScript starts to shine. Uncertainty makes room for certainty. This is invaluable. Instead of needing to browse a tree of functions to discover what properties an object needs, we can look up its type or interface. We can write code as if data types are known, avoiding over-defensive code you’ll find in plain JavaScript. Should a type-related bug occur, we know to look at the boundary where the value is validated, parsed, or sanitized, unless someone accidentally type cast the value to something it’s not. Either way, we know where to look or what to look for.&lt;/p&gt;
&lt;p&gt;Type annotations enable us to be less distrustful about data. It forces us to think about semantics and data structures and lets us write naive code because data types are known. It gives peace of mind.&lt;/p&gt;
&lt;h2&gt;Is it worth it?&lt;/h2&gt;
&lt;p&gt;There’s a cost to all this. Although it removes runtime noise, TypeScript does add syntactical noise that can get incredibly complex. Moreover, the error messages can be puzzling, it adds another dependency to the toolchain, and it’s yet another thing people spend time and energy on that could’ve been spent on learning about (web) standards.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It remains fair to ask the question: is it really worth it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TypeScript eliminates some uncertainty experienced during development. The effects of that are hard to measure. A (potential) increase in time consumed is one, but there are many more: the number of bugs avoided of any type, the impact it has on the overall code quality, and how it affects the mental state of the developer and their productivity.&lt;/p&gt;
&lt;p&gt;The unsatisfying answer is: I don’t know if TypeScript is &lt;em&gt;really&lt;/em&gt; worth it, and I don’t think there’ll be a definitive answer. I suppose it’s a gamble, a gut feeling, where the outcomes of all choices are ambiguous.&lt;/p&gt;
&lt;p&gt;My gut tells me it doesn’t vastly inflate development time or complexity, and it helps me write better code with more confidence. That’s good enough for me.&lt;/p&gt;
</content:encoded><category>js</category><category>tools</category><author>Tim Severien</author></item><item><title>2022 in review</title><link>https://tsev.dev/posts/2022-12-25-2022-in-review/</link><guid isPermaLink="true">https://tsev.dev/posts/2022-12-25-2022-in-review/</guid><pubDate>Fri, 30 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2022 is almost over and that’s a great time to reflect! I could dilly-dally about how I’m finally using React and how awesome Astro is, but the truth is that this year was largely defined by my burnout. Fortunately, out of that dark place came newly gained knowledge, skills, and motivation.&lt;/p&gt;
&lt;h2&gt;Mental health&lt;/h2&gt;
&lt;p&gt;The year started with stress-related symptoms. It took me a while to realize &lt;a href=&quot;/posts/2022-07-21-lessons-learned-from-burnout&quot;&gt;I was burned out&lt;/a&gt;, and I urgently needed a break from work to get rid of those symptoms.&lt;/p&gt;
&lt;p&gt;Although I wish none of this had happened, I learned so much about mental health and myself. In the weeks leading up to the break, I started to increasingly pay attention to myself and my mental state, and when one sits home alone pondering, reflection became a routine.&lt;/p&gt;
&lt;p&gt;A not surprising realization was that I was unhappy where I worked at, but I did come to understand neither I nor my colleagues could change that. In fact, I was drained after unsuccessfully trying to make things better for myself, my peers, and the company. The alternative was to find a new job, which is exactly what I did.&lt;/p&gt;
&lt;p&gt;The change in jobs was beneficial in a multitude of ways. Moving from the agency space to a small product development startup is a big change. Better culture, less bureaucracy, no faux deadlines, success is measured objectively instead of based on the perception of a workgroup, etc. All of this significantly reduces concerns and frustration, so definitely a big win for my mental health — I don’t think I’ll ever go back to an agency.&lt;/p&gt;
&lt;p&gt;I’ve become pretty good at recognizing signs and bad habits, both in myself and others. It’s tricky to figure out whether these are actually bad for others, though. Some people are just more resilient to stress, deal with it in their own way, or are already well aware of any knowledge I think I have to offer. To be honest, I’d rather be the person that reminds others of their mental health too often than not enough.&lt;/p&gt;
&lt;p&gt;The nice thing about working in a small startup is that everyone greatly contributes to culture, and I make sure to do my part by raising mental health. The principles “mental health is also health” and “health before work” are frequently mentioned. I do wonder we’ll stick to these principles when we face difficulties, like an upcoming deadline. Moments like these will determine whether they are principles we don’t want to let go of or mere guidelines that we end up following only when things go well.&lt;/p&gt;
&lt;p&gt;Although I have no regrets going fully remote, I do miss the social interactions I had in an office. I miss playing games and going out for a bite and drinks with friends on a whim. Nothing a little bit of initiative can’t fix, though!&lt;/p&gt;
&lt;h2&gt;A larger-than-life goal&lt;/h2&gt;
&lt;p&gt;In the face of a pandemic, repeated threats of nuclear annihilation from a silly little man and his propaganda goons, and mental health issues, one thinks about death, life, legacy, and meaning.&lt;/p&gt;
&lt;p&gt;Being an atheist, I don’t have a god, prophet, oracle, or book I can turn to when I’m wondering what the point is of it all. I don’t think there’s inherent meaning to life. Our actions are mere ripples that add up to, yet fade into, the waves of the ocean that makes all that is and ever will be. Despite this grim view, I also acknowledge the human experience with thoughts and feelings, and a desire to find meaning.&lt;/p&gt;
&lt;p&gt;I want to make lives better and improve quality of life. I could save animals or travel to a developing nation and develop water sources, sanitation, schools, and other facilities that improve quality of life by a huge leap. Although I admire selfless acts, that’s not something I’d enjoy at all. Instead, I try to find overlap between improving lives and my interests, which mainly boils down to software (development) and sharing knowledge.&lt;/p&gt;
&lt;p&gt;Slowly but surely I’m picking up pace.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I’m trying to blog more frequently and candid, starting with &lt;a href=&quot;/posts/2022-07-21-lessons-learned-from-burnout&quot;&gt;my article on burnout&lt;/a&gt;. To this day, I hope that my experience of this life-changing event saves someone from having going through the same, or at least gives someone hope that they’ll get better.&lt;/li&gt;
&lt;li&gt;I (soft-)launched &lt;a href=&quot;https://ijsjes.dev&quot;&gt;ijsjes.dev&lt;/a&gt;, a JavaScript blog to get people better at developing software with JavaScript and TypeScript. There’s a strong focus on practical tips, while also frequently touching higher-level concepts. I hope this enables people to build a product or make a career, so they can sustain themselves and their families. It would be nice if I could grow this into something larger than a blog, but let’s get some actual readers, first.&lt;/li&gt;
&lt;li&gt;Finally, I continue to lift fellow engineers up at work while I try to get better at leading and mentoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not everything was profound and life-changing this year.&lt;/p&gt;
&lt;h2&gt;Technology&lt;/h2&gt;
&lt;p&gt;For brevity, I’ll limit to the tech I spent most time using and learning, which is React.&lt;/p&gt;
&lt;p&gt;I never jumped the hype train until halfway this year. Well, I suppose that’s an overstatement as I’m not particularly hyped: it’s pretty good, but not significantly better than other client-side rendering libraries. I’ll elaborate on this in another blog post!&lt;/p&gt;
&lt;p&gt;It did confirm something I always thought: critics attribute some flaws (e.g. bad accessibility or UX) to the library, rather than its authors. There are definitely challenges unique to React, but nothing that prevents us from writing good software. It’s definitely a fun addition to my toolbox.&lt;/p&gt;
&lt;h2&gt;Maui the cat&lt;/h2&gt;
&lt;p&gt;Meet Maui, Waker of Humans, Breaker of Breakables. He is the most active, most playful, furriest, and naughtiest family member.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/maui-the-cat.jpg&quot; alt=&quot;A close-up of a black cat with a mostly white face, long whiskers and brows, and lynx ears&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We specifically looked to adopt a cat slightly older than a kitten as I’d have to get work done and never raised a cat before. Maui was 1.5 years old when we got him. Despite his age, he’s still a handful as he actively looks for stuff to knock over or sink his claws in.&lt;/p&gt;
&lt;p&gt;When he’s not shortening the lifespan of our furniture, he’s super cute and cuddly. He reminds me to take breaks from work a couple of times a day, which is probably for the best. I’d argue he definitely makes my day brighter.&lt;/p&gt;
&lt;p&gt;My partner and I searched for names of trickster deities, as cats are our mischievous overlords. Loki comes to mind, but that was a bit too common, so we looked further. Eventually we found the &lt;a href=&quot;https://en.wikipedia.org/wiki/M%C4%81ui_(mythology)&quot;&gt;Polynesian hero&lt;/a&gt; Māui, who isn’t worshipped or a deity, but hero is close enough. Being almost an anagram of &amp;lt;span lang=&quot;nl&quot;&amp;gt;&lt;em&gt;miauw&lt;/em&gt;&amp;lt;/span&amp;gt;, the Dutch &lt;a href=&quot;https://en.wikipedia.org/wiki/Onomatopoeia&quot;&gt;onomatopoeia&lt;/a&gt; of a cat’s call, was definitely something that I really liked. Also, the name reminds me to sing Disney’s Moana songs every day, like &lt;a href=&quot;https://youtu.be/79DijItQXMM&quot;&gt;You’re Welcome&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;2023&lt;/h2&gt;
&lt;p&gt;YouTuber CGP Grey speaks of yearly themes in the video &lt;a href=&quot;https://youtu.be/NVGuFdX5guE&quot;&gt;Your Theme&lt;/a&gt;. To summarize: a theme directs you towards making good decisions along the way instead of setting one ambitious goal and trying to stick to it. Grey also mentions that a year is a long period, too long to feel urgency. I’d like to give this a go and pick a theme every quarter.&lt;/p&gt;
&lt;p&gt;In no particular order, I want to exercise frequently again, develop my leadership and mentoring skills, get &lt;a href=&quot;https://ijsjes.dev&quot;&gt;ijsjes.dev&lt;/a&gt; out in the world, and maybe start on some indie product development. All of these topics are more specific than CGP Grey recommends, but I feel they’re broad enough and specific enough for quarterly themes.&lt;/p&gt;
&lt;p&gt;So, let’s get this year over with and get on with the next!&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>Bye bye, Twitter</title><link>https://tsev.dev/posts/2022-12-16-bye-twitter/</link><guid isPermaLink="true">https://tsev.dev/posts/2022-12-16-bye-twitter/</guid><pubDate>Fri, 16 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After over 12 years being more or less active on Twitter, I logged out everywhere and deleted the app.&lt;/p&gt;
&lt;h2&gt;How Twitter changed&lt;/h2&gt;
&lt;p&gt;During the pandemic, the toxicity of people on the platform accelerated significantly, and so did bot activity and dumbassery. Many people that I follow are harassed, people and bots continuously try to dispute facts, and they try to sow doubt with lies and cherry-picked information.&lt;/p&gt;
&lt;p&gt;Enter Musk. The ‘genius’ entrepreneur. The man that &lt;a href=&quot;https://www.forbes.com/sites/nicholasreimann/2022/11/19/musks-trump-twitter-poll-hits-11-million-votes-most-want-ex-president-back-on-platform/&quot;&gt;reinstated Trump’s account based on a Twitter poll&lt;/a&gt;. The man that had &lt;a href=&quot;https://www.ft.com/content/ad4efd13-3e9e-4e76-968e-6d27b2c9346a&quot;&gt;engineers work over the weekend to ship Twitter Blue&lt;/a&gt; in an attempt to commercialize a blue checkmark, which completely backfired as it enabled random accounts to appear authentic when they weren’t. Quickly after he took over, I saw another jump in bot activity, and suddenly I get crypto and NFT spam now. What a legend. What a genius.&lt;/p&gt;
&lt;p&gt;Although I can forgive some inconveniences in an attempt to increase revenue or make a business economically sustainable, the poor leadership and ethically questionable decisions Musk have shown in recent months, isn’t something I want to support in any way. Besides, Twitter has become objectively worse, now that the floodgates of bots, harassment, and misinformation have opened.&lt;/p&gt;
&lt;p&gt;But there are many more reasons why I don’t want to support this new CEO. He shares misinformation and &lt;a href=&quot;https://www.nbcnews.com/tech/internet/elon-musks-twitter-beginning-take-shape-rcna58940&quot;&gt;brings back QAnon and far-right accounts&lt;/a&gt;, influences financial markets in his favour, promotes fake currencies that produce off-the-chart emissions, and &lt;a href=&quot;https://www.theguardian.com/technology/2022/dec/05/neuralink-animal-testing-elon-musk-investigation&quot;&gt;is responsible for killing more animals than needed&lt;/a&gt; at Neuralink. While I’m writing this article, he’s also &lt;a href=&quot;https://www.nytimes.com/2022/12/15/technology/twitter-suspends-journalist-accounts-elon-musk.html&quot;&gt;banned accounts that track private jets and a handful of journalists&lt;/a&gt; for ‘doxxing’ even though it’s &lt;a href=&quot;https://globe.adsbexchange.com/?icao=a835af&quot;&gt;his jet (N628TS)&lt;/a&gt; that sends out an Automatic Dependent Surveillance-Broadcast (ADS-B) signal for everyone to see. I wonder what &lt;a href=&quot;https://twitter.com/joinmastodon&quot;&gt;@joinmastodon&lt;/a&gt;, an account for a competing platform, did to get banned. Musk wants Twitter to be an unmoderated shit show, yet he clearly moderates in his own interest.&lt;/p&gt;
&lt;p&gt;I apologize for turning this post into a rant.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Takes a deep breath&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a great example of the dangers of having a public space in the hands of a single person.
&lt;a href=&quot;https://nitter.pussthecat.org/elonmusk/status/1595473875847942146&quot;&gt;Pardoning accounts&lt;/a&gt; because no law was violated and a Twitter poll said so, while banning others for sharing information someone doesn’t like, is quite arbitrary.&lt;/p&gt;
&lt;p&gt;Make sure to also read Hidde de Vries’ much better articulated post: &lt;a href=&quot;https://hidde.blog/twitter-exodus/&quot;&gt;Is this the last exodus from Twitter?&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What I’ll miss&lt;/h2&gt;
&lt;p&gt;I primarily used Twitter to engage with a relatively small group of people. Luckily, a significant part of that group is also moving away, but not all of them. I’ll definitely miss out on some good content, but that’s an inconvenience I’m happy to accept.&lt;/p&gt;
&lt;p&gt;Unfortunately, I can’t take my 800+ followers with me. I’m sure social media-savvy people will laugh at that number as it’s absolutely nothing for someone that puts in effort, but as someone who doesn’t actively seek an audience, I’m honoured to have acquired that many followers.&lt;/p&gt;
&lt;h2&gt;Now what?&lt;/h2&gt;
&lt;p&gt;A couple of years ago, I created a Mastodon account. At the time, I was intrigued by the idea of an alternative that was federated, open-source, and wouldn’t sell data to advertisers. Once Twitter became Musk’s, many friends gave that another go and naturally, I followed. Now that I’ve actively used it for a couple of weeks, I realize there’s much to improve, but the “dumb” timeline that isn’t curated or riddled with advertisements, is fantastic.&lt;/p&gt;
&lt;p&gt;It also has, as a Gen Z person might say, good vibes. So far, people have a positive attitude, which is a welcoming change of scenery. Considering that different social platforms have different cultures, I definitely hope Mastodon keeps the welcoming one it has now.&lt;/p&gt;
&lt;p&gt;I won’t say Mastodon is perfect. There’s no business model, so might only drain instance owners of money. In fact, they can disconnect their servers on a whim, so there are definitely other concerns to consider. Additionally, it might not work for people who do what to grow an audience, as there are no features for that specifically and there’s no algorithm to trick.&lt;/p&gt;
&lt;p&gt;Twitter crumbling down under bad leadership and the risks of depending on individuals are good reminders that &lt;a href=&quot;https://youtu.be/X3SrZuH00GQ?t=380&quot;&gt;“nothing in this world is certain, except the death of social media, and taxes”&lt;/a&gt;. To reclaim ownership of my data, I’ll definitely look into &lt;a href=&quot;https://indieweb.org&quot;&gt;indie web&lt;/a&gt; and &lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;POSSEing&lt;/a&gt; my content, but that’s a story for another day.&lt;/p&gt;
&lt;p&gt;So, &lt;a href=&quot;https://joinmastodon.org/servers&quot;&gt;find and join a Mastodon server&lt;/a&gt;, and hang out with us. If you want, you can follow &lt;a href=&quot;https://mastodon.social/@timsev&quot;&gt;@timsev@mastodon.social&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>When principles degrade performance — the memory problem of immutability</title><link>https://ijsjes.dev/posts/2022-10-25-when-principles-degrade-performance-the-memory-problem-of-immutability/</link><guid isPermaLink="true">https://ijsjes.dev/posts/2022-10-25-when-principles-degrade-performance-the-memory-problem-of-immutability/</guid><pubDate>Tue, 25 Oct 2022 00:00:00 GMT</pubDate><category>js</category><author>Tim Severien</author></item><item><title>Design patterns: Singleton</title><link>https://ijsjes.dev/posts/2022-08-26-singleton/</link><guid isPermaLink="true">https://ijsjes.dev/posts/2022-08-26-singleton/</guid><pubDate>Fri, 26 Aug 2022 00:00:00 GMT</pubDate><category>js</category><author>Tim Severien</author></item><item><title>What makes JavaScript so popular</title><link>https://ijsjes.dev/posts/2022-08-06-what-makes-javascript-popular/</link><guid isPermaLink="true">https://ijsjes.dev/posts/2022-08-06-what-makes-javascript-popular/</guid><pubDate>Sat, 06 Aug 2022 00:00:00 GMT</pubDate><category>js</category><author>Tim Severien</author></item><item><title>3 methods to run code conditionally without if-statements</title><link>https://ijsjes.dev/posts/2022-08-03-3-methods-to-run-code-conditionally/</link><guid isPermaLink="true">https://ijsjes.dev/posts/2022-08-03-3-methods-to-run-code-conditionally/</guid><pubDate>Wed, 03 Aug 2022 00:00:00 GMT</pubDate><category>js</category><author>Tim Severien</author></item><item><title>Lessons learned from burnout</title><link>https://tsev.dev/posts/2022-07-21-lessons-learned-from-burnout/</link><guid isPermaLink="true">https://tsev.dev/posts/2022-07-21-lessons-learned-from-burnout/</guid><pubDate>Thu, 21 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last ~2.5 years were wild. They sure were for me, anyway. During our collective trauma from practising social distancing because of a pandemic, I also had burnout. Not the kind most people talk about — where you’re mildly exhausted and need a holiday — but the severe kind. Before this, I didn’t know what burnout was or what it’s like, because few people talk about it. Let’s break taboos! I want to tell you how it happened, what it was like, what helped me get through it, and what I and others could’ve done to avoid it.&lt;/p&gt;
&lt;h2&gt;Escapism and guilt, escapism and guilt&lt;/h2&gt;
&lt;p&gt;My burnout didn’t happen overnight.&lt;/p&gt;
&lt;p&gt;A daily worry of mine is the existential crisis: climate change. Or climate catastrophe. I frequently think about whether the changes we’re experiencing are reversible and what their impact is and will be. How it will kill, traumatize, and displace people. My parents might experience some extreme weather, and many of us may have to deal with drought and minor floods more regularly, but it’ll be life and death for less privileged people and for the generations after us. Researches also keep discovering new effects caused by climate change. In developed (and developing) nations, we collectively and reluctantly consume massive amounts of fossil fuel-based energy, even though we’ve known the impact for decades[^1]. The measures we’re taking now should’ve been done decades ago. Why are we so late to act? Can we reverse it, or at least prevent it from worsening? Why do boomers still think it’s a fucking solar or orbital cycle? And why the fuck do we waste energy on useless shit like proof-of-work blockchains?!&lt;/p&gt;
&lt;p&gt;Then the SARS‑CoV‑2 pandemic hit. I didn’t mind sitting at home 24/7, but I was concerned about the physical and mental well-being of loved ones and the vulnerable. Additionally, it somehow became an opportunity for anti-government and anti-science QAnon crazies to thrive and spread the most far-fetched ideas as facts and run misinformation campaigns. I’m sure you haven’t escaped the “it’s just a flu” and “the vaccine will kill you” nonsense.&lt;/p&gt;
&lt;p&gt;Amidst the crises above, I struggled at the agency I worked at. Without getting into details, I grew cynical toward the company and felt bad decisions were being made. Moreover, I worked on this big refactor/rebuild. When I started, I had a blast. The joy was gradually replaced with worry, as progress was slower than I wanted it to be. I was also convinced I was the only one with the knowledge to carry out the work, so I felt a weight on my shoulders to become more productive. Although my team wanted to help where they could, I worried that their help, or anyone’s, would slow things down instead.&lt;/p&gt;
&lt;p&gt;Contrary to what I wanted, my productivity progressively worsened as I demanded more of myself. Every day, I’d start frustrated thinking about what I had to do, followed by a cycle of escapism and guilt. I’d work until exhaustion, unwind by allowing some slack, and then get back to work feeling guilty about my distraction until I felt exhausted again. Being behind schedule, I started working longer days (sometimes up to 12 hours) and was unable to fall asleep for hours, worrying about how much I had to do the following day. Despite falling asleep late, I’d wake up early in the morning (4-5 a.m.), unable to fall back asleep. Instead, I’d start working and lie to myself that I’d only work for 8 hours and stop at lunchtime. In addition to sleep deprivation, irregular sleep rhythm, and continuous guilt, I experienced daily headaches and was frequently dizzy. Morning walks, fresh air, the medication the doctor prescribed, stomach breathing, meditation, nothing seemed to work.&lt;/p&gt;
&lt;p&gt;I kept going until the refactor/rebuild was done, switched teams within the same organization, and started on a promising greenfield project; just the break I needed, I remember thinking. The sleep improved, headaches quickly faded, and no more dizziness.&lt;/p&gt;
&lt;p&gt;Although promising, the greenfield project had a tight deadline among many other problems. The ambition of setting the example of how a project ought to be quickly turned into a “let’s get things done”-mentality. Like the last project, I required myself to work faster than I could manage. As the only front-end developer in the team, I again thought involving anyone else would slow things down rather than speed things up. The cycle of escapism and guilt returned, and so did the physical manifestations of stress.&lt;/p&gt;
&lt;p&gt;After some weeks of headaches and dizziness, I was fed up. I got in touch with the company’s confidant to tell how I felt, and kinda vent about work. After several check-ins, we agreed I needed to catch my breath by working less. I went from 8 to 7, and later to 6 hours a day. That was a huge relief, but did little otherwise. In fact, it may have worsened the situation, as I now felt guilty working less and wanted to get even more work done in 6 hours. I know that’s not how it works, but clearly, I wasn’t thinking straight.&lt;/p&gt;
&lt;p&gt;In one of our weekly calls, I mentioned how things affected my personal life. I was fatigued, irritable, fell asleep right after work, couldn’t sleep at night, and had no energy for hobbies or household chores. All of that was going on for months. That’s when I was told I had to stop working immediately. I remember thinking about the work I had to do, but the confidant was resolute.&lt;/p&gt;
&lt;h2&gt;Burnout and recovery&lt;/h2&gt;
&lt;p&gt;There I sat, at home. For weeks. At the time, I told people I was home for “stress-related conditions” or “an almost burnout.” A bit out of shame, but mostly because I just wasn’t ready to admit it to myself yet. Especially in the beginning, I would talk myself into guilt trips like how I was letting everyone down and had to stop being dramatic.&lt;/p&gt;
&lt;p&gt;Some days were better than others. I had days when I would wake up between 8 and 9 a.m., performed two household chores, and lie on the sofa for the rest of the day. On bad days, I’d stay in bed most of the day, force myself out to eat breakfast at 3-4 p.m. and lie on the sofa to sleep or watch TV. Sometimes I’d find the motivation to play a game for about 15 minutes at most. Normally, I don’t mind grinding challenging games for hours on end, but during my burnout, I changed their settings until they were free of any challenge. Despite that, I would still feel overwhelmed within a couple of minutes.&lt;/p&gt;
&lt;p&gt;Hearing how other people experienced their burnout was comforting and insightful. Knowing other people experienced something similar and recovered gave me hope that I would recover in time, too. I did find that burnout severity varies greatly and people experience it very differently. Some people are just fatigued and need a couple of weeks to recharge. Others mentioned a daily routine of crying and sleeping.&lt;/p&gt;
&lt;p&gt;I was somewhere in between; fatigue would set in before lunch (sometimes before breakfast), and I was extremely bored, yet couldn’t find the energy to do anything at all. Sometimes I’d ask myself, “what’s wrong with me? Why am I like this?” Hating my condition yet feeling powerless to change it did open the floodgates once or twice. It was a very confusing experience.&lt;/p&gt;
&lt;p&gt;I vividly recall I was unable to perform some tasks. I always try to be mindful of what I do, even when it seems trivial. One day I had to pack some bags with gifts that I had to carry for a bit, so I tried to distribute the weight evenly. With the burnout, however, this simple task felt extremely challenging; I just couldn’t do it! I kept swapping items between bags as if I was brute-forcing every combination, and it was infuriating. My girlfriend had to calm me down and help me do it.&lt;/p&gt;
&lt;p&gt;Anything computer-related was out of the question. Creative coding is, or rather was, one of my favourite hobbies. After some weeks of immense boredom, I gave that a go. That session lasted about 15 minutes until I crashed on the couch again. Like anything else, I couldn’t manage. Being fed up that fast with hobbies sucks big time. I felt I finally had all the time in the world, but couldn’t spend it.&lt;/p&gt;
&lt;p&gt;One major breakthrough was deciding to get a new job. Making the decision alone lifted a huge weight off my shoulders. Before my burnout, I was casually browsing for new jobs for anything better, but during the burnout, I decided I’d move on regardless. Looking for a new job was the only thing I managed to find energy for. Fatigue would still kick in, so I took my time.&lt;/p&gt;
&lt;p&gt;After approximately two months, I went back to work (at the old job). I’ve been told some people are back on their feet in a week or two, while others need half a year, so I’m glad I felt okay-ish after (just) two months. We started with 2 hours a day and slowly increased that number every week, depending on how I felt. This was standard procedure for all burned-out employees. In hindsight, I think I could’ve used a longer break as working felt like a lot at the time, but it kinda worked out. The pace was slow enough for me to continue my recovery, and the prospects of a new job helped me distance myself from work frustrations.&lt;/p&gt;
&lt;h2&gt;The post-burnout scars&lt;/h2&gt;
&lt;p&gt;Some friends who had burnout as well, told me they eventually became their old selves again, including the bad habits that got them into the situation in the first place. I can’t wait for my energy level to return to what it used to be. I’ve come a long way, but I still experience some effects from burnout.&lt;/p&gt;
&lt;p&gt;I used to be hyped about new tech. I’d read tons of newsletters and articles, and watch videos to stay up-to-date with the latest specifications, libraries, and frameworks. One time I printed out the Web Content Accessibility Guidelines to read on the beach on my holiday, which people often joked about. If I had spare time on my hands, I’d dive into the deep unknown and try out something new on a side project. For the past two years, I barely have. At some point, I started to follow some tech news, but am very selective. It’ll probably get better, but I intend to continue limiting my intake so it won’t overwhelm me.&lt;/p&gt;
&lt;p&gt;Most hobbies are coming back slowly. Playing video games is the only one I’m comfortable with again, but I’m cautious with very challenging games. Coding in my spare time was out of the question long after I got back to work, but that’s also coming back.&lt;/p&gt;
&lt;p&gt;Easy tasks are easy again. Complex tasks can be tough but manageable. I also look forward to and enjoy spending time with friends and family. Maybe even more than I used to. Heck, nowadays I get out of bed easily because I look forward to work. How about that!&lt;/p&gt;
&lt;p&gt;Unfortunately, burnout remains a constant nag. Some people that have experienced burnout can show post-traumatic stress disorder-esque behaviour, and I can relate to that. Whenever I get stressed, I get the urge to escape. At those times, it’s as if insignificant amounts of stress may cause huge setbacks, and I need to do everything in my power to avoid that. It’s a recipe for overreacting and makes me (over)cautious.&lt;/p&gt;
&lt;p&gt;When I got back to work, my heart started racing just thinking about work. Less significantly but still noticeable is that my music taste has altered. I used to listen to intense and high-energy music much more than I do now. It illustrates perfectly how I try to eliminate whatever may increase stress, no matter how small. Classic, ambient, reggae, and lo-fi over metal and drum and bass. Writing this article is difficult: I’m reliving the entire thing, and it sucks. Like everything else, things are getting better every day.&lt;/p&gt;
&lt;h2&gt;What I’ve learned&lt;/h2&gt;
&lt;p&gt;Although a bit of self-diagnosis here, I think my burnout was &lt;em&gt;occupational burnout&lt;/em&gt; per the World Health Organization definition — where the cause is chronic workplace stress. As I mentioned earlier, there were other stressors, but I was able to cope with those until work got extremely stressful.&lt;/p&gt;
&lt;h3&gt;A way out of burnout&lt;/h3&gt;
&lt;p&gt;Unfortunately, there’s no clear-cut plan to get out of it. I haven’t discovered the magic cure, but some things help.&lt;/p&gt;
&lt;p&gt;Stress isn’t bad, but prolonged stress is, as your body needs time to recover to baseline levels. I felt I experienced stress for way too long and thus needed a long time to recover. Because of that, I first decided to avoid whatever were mental drains or takes a lot of energy. Relaxation is probably what most people will recommend. Unfortunately for me, this eliminated many hobbies.&lt;/p&gt;
&lt;p&gt;A friend sent me a talk by Arthur Doler[^2] in which Arthur suggests stopping denying burnout, finding a new job, creating work/life boundaries, and seeking involvement elsewhere. I remember finding the advice weird and a bit extreme initially, although he adds nuance to it, and in hindsight, I must agree.&lt;/p&gt;
&lt;p&gt;It took me a while to come to terms with my condition. Talking about it with friends and sharing experiences helped tremendously! As I recognized more of their stories in myself, I couldn’t continue denying it. And they told me of recovery, too, which gave me hope.&lt;/p&gt;
&lt;p&gt;Finding involvement elsewhere is something I’ve done often to cope with little autonomy; I try to regain it elsewhere, often in a side project. I didn’t realise I was doing this until I watched a talk by Laralyn McWilliams[^3], in which she expands on this idea by creating a game as a side project to regain control people might miss at their workplace. Unfortunately, during my burnout, I was too fatigued to do anything at all. It may work for others, though.&lt;/p&gt;
&lt;p&gt;After deciding I wanted to find a new job, I had something to look forward to and felt I would be rid of the stressors that got me in this situation. This significantly improved my attitude towards work in general, but also towards the job I was about to leave — the cynicism was gone. I now see why people stay, but am still convinced I had to move on. This choice definitely gave me peace of mind and accelerated recovery.&lt;/p&gt;
&lt;h3&gt;Stress blinds you&lt;/h3&gt;
&lt;p&gt;When times get rough, I forget to take care of myself and my world starts to circulate around this stressful thing. All else becomes background noise. For example, I know I’m not a machine with a continuous output. I know working more doesn’t mean I’ll get more things done. But under stress, I get less forgiving. Although setting our minds to it and focussing may improve productivity for a moment, it’s not something that lasts long. Being so caught up with the stressor blinded me to how to get out of trouble.&lt;/p&gt;
&lt;p&gt;Indicators may help us recognize when we’re spiralling down and may get us out of that hyperfocused state or see the need for change. I suppose they are different for everyone, but mine could include monitoring my working heart rate. I often had a heart rate of 80-90 throughout the working day. For an office job, where the body is typically in a sitting/resting position, that’s a shockingly high rate. I might be able to turn that into a passive indicator — a system that monitors my heart rate and notifies me when it’s abnormally high — which has its benefits.&lt;/p&gt;
&lt;p&gt;Other indicators can be colleagues, partners, friends, or family members that might see you’re struggling and tell you. If we’re talking about occupational stress, colleagues are in the best position to help you out. For example, a manager could’ve seen me struggling and given me the help I refused.&lt;/p&gt;
&lt;h3&gt;Offering help is not helping&lt;/h3&gt;
&lt;p&gt;This is more of a social concern than a burnout-specific lesson, but hear me out.&lt;/p&gt;
&lt;p&gt;Whenever I raised my stress, “I’m here if you need help” was the go-to reply. This is well-intended, I’m sure. When I had a lot of stress, however, I was too caught up, felt too guilty to ask, and often failed to see how anyone could help me out. I’m not pointing any fingers, but it would’ve made a huge difference if someone did intervene and got me the help I didn’t know I needed.&lt;/p&gt;
&lt;p&gt;Anyway, I want to actually help people where I can, instead of just offering it. If I notice someone showing signs of stress or burnout, I’ll definitely check in with them and try to eliminate whatever bothers them.&lt;/p&gt;
&lt;h3&gt;Prevention is better than cure&lt;/h3&gt;
&lt;p&gt;A very cheesy saying, I know, but it’s true!&lt;/p&gt;
&lt;p&gt;There’s this (unwritten) rule that our actions in our private life shouldn’t affect work negatively. Some contracts explicitly state it. We should have the same standard the other way around; work shouldn’t affect our life in a bad way. Just like I won’t pull an all-nighter on a new game and show up at work exhausted, I no longer want to work until I have no energy left to live my life. I don’t know how yet.&lt;/p&gt;
&lt;p&gt;Deadlines and limited autonomy are the two things I need to look out for, as I can beat myself up trying to achieve the impossible. I’m transparent about my thoughts about whatever, including deadlines, but warning others we’re not going to make it only to be somewhat ignored doesn’t relieve any pressure. I guess I’d rather be the bad guy who insists on postponing impossible deadlines until they are postponed or lifted.&lt;/p&gt;
&lt;p&gt;Lastly, I learned, to reevaluate my happiness more. Especially since we spend much of our time working — and burnout is often occupational in nature — we must enjoy our job. At least enough to get out of bed. Dan Pink says the three factors in achieving personal satisfaction are: autonomy, mastery, and purpose[^4]. My old job scored fairly good (but not great) on two of three factors. Additionally, I realized the work wouldn’t change the way I wanted it to, and the next project would have the same if not worse constraints: I needed to move on. To make sure I actually get to reflect, I might schedule a monthly ‘meeting’.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Burnout sucks big time. I don’t recommend it to friends. 1 out of 5 stars; big nope. I did learn how to deal with work stress a bit better.&lt;/p&gt;
&lt;p&gt;The newly found insights are cool, but I hate that I lost passion and a bunch of hobbies. I’m not stoked about that I evaluate the mental health risk for pretty much everything, but I might be able to shape that into something healthy. But most of all, I hate how my condition affected the people around me. This could’ve been avoided if I’d learned to manage stress earlier.&lt;/p&gt;
&lt;p&gt;I’m very grateful for all the help I got from my colleagues, friends, and most of all: my partner. Things would’ve been much worse without their help. They’ve listened, given me advice, and helped me out in a time I’m sure I wasn’t fun to be around.&lt;/p&gt;
&lt;p&gt;I hope this lengthy post is somewhat insightful. Trust me when I say it sucks to be mentally broken, so be sure to look into stress management techniques — I can’t &lt;em&gt;stress&lt;/em&gt; this enough, haha! I highly recommend watching Arthur’s talk[^2] next.&lt;/p&gt;
&lt;p&gt;Be safe! If you have questions or just want to chat: find me on &lt;a href=&quot;https://twitter.com/timseverien&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[^1]: &lt;a href=&quot;https://www.climatefiles.com/shell/1988-shell-report-greenhouse/&quot;&gt;1988 Shell Confidential Report “The Greenhouse Effect”&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[^2]: &lt;a href=&quot;https://www.youtube.com/watch?v=-XYK8ulQId0&quot;&gt;You&apos;re Not Just Tired: The Psychology of Burnout&lt;/a&gt; by Arthur Doler on Codemash conference 2022.&lt;/p&gt;
&lt;p&gt;[^3]: &lt;a href=&quot;https://www.youtube.com/watch?v=zfJ9LLZQ9jo&quot;&gt;Battling Burnout: The Side Project Ritual&lt;/a&gt; by Laralyn McWilliams on GDC 2020&lt;/p&gt;
&lt;p&gt;[^4]: &lt;a href=&quot;https://www.youtube.com/watch?v=u6XAPnuFjJc&quot;&gt;RSA ANIMATE: Drive: The surprising truth about what motivates us&lt;/a&gt; by Dan Pink (and illustration by RSA).&lt;/p&gt;
</content:encoded><category>personal</category><author>Tim Severien</author></item><item><title>The perfect project management framework doesn’t exist</title><link>https://tsev.dev/posts/2022-01-28-the-perfect-software-project-methodology-doesnt-exist/</link><guid isPermaLink="true">https://tsev.dev/posts/2022-01-28-the-perfect-software-project-methodology-doesnt-exist/</guid><pubDate>Tue, 25 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you work with them, you undoubtedly heard a software engineer complain about project management methodology. “This isn’t Scrum; it’s water-scrum-fall,” they’ll say. Although I agree that we should improve the process continuously, and there’s always room for improvement, I’m afraid many of us are wrong about what it ought to be and how we’re getting there.&lt;/p&gt;
&lt;p&gt;We’ve all been in that situation. That big project where the customer’s requirements are set in stone to the implementation level. Nonetheless, you’re told “we do Scrum”; the Scrum ceremonies are all there. Or that project without Product Owner, is not involved, lack the mandate to make decisions about the product, or has absolutely no knowledge about the requirements.&lt;/p&gt;
&lt;p&gt;In situations like these, a team member will take the Agile Manifesto or collect some Medium posts about Scrum, say how bad our project management is and plea for change. Being conscious about it and seeing room for improvement is actually wonderful. However, we have to be equally aware of which changes are improvements.&lt;/p&gt;
&lt;p&gt;When discussing project management systems, we often grab our favourite approach and compare them to the status quo. When the actual workflow deviates from that framework, it’s typically perceived as bad. Or in other words: the framework is the goal. This shouldn’t always be the case. Ironically, it’s perceived as the ideal solution in an industry where “it depends” is the comic (but correct) reply to most problems in tech. Similarly, one may advocate for a flexible and adaptive approach and yet be reluctant to let their workflow adapt to their continuously evolving situation.&lt;/p&gt;
&lt;p&gt;There’s no miracle solution for every team in every organization, as each one is vastly different. I mostly worked for agencies that tried to do Scrum. Tried, because most clients aren’t agile and need to know what they’ll get to get their budget approved by the finance department. This inherently affects how agile we can be and lo and behold, we ended up with water-scrum-fall. There are two issues with this telltale example that propagate into a myriad of symptoms.&lt;/p&gt;
&lt;p&gt;First, we lie about our methodology. Sometimes it just isn’t Scrum, no matter how much we want it to be. Instead of becoming comfortable with the idea that we can adapt towards an optimum, a strong pull towards the framework we claim to use remains. A framework that might not even be the best fit, I might add. If the team comes to terms with the fact that a given framework will never work, the members can widen their views and either look at other frameworks or optimize individual problems, even if it’s opposite to what their favourite framework dictates. Note that we shouldn’t lie down without a fight; sometimes, through great perseverance, a client might come through.&lt;/p&gt;
&lt;p&gt;The second issue is that a blend of frameworks (Scrum and Waterfall) might be counterproductive. When the specifications and features are fixed, and we’re working towards a big bang release, odds are the project doesn’t need the iterative cycle of Scrum, and we’re crunching features instead. So pretending to build that metaphorical bicycle — when in fact we’re already building the race car the client wants — isn’t helping. It’s just noise. Watch out for practices that void others.&lt;/p&gt;
&lt;p&gt;So, what should you do?&lt;/p&gt;
&lt;p&gt;Be honest with yourself and your team. Development teams, management teams, and external factors like stakeholders are far too diverse and complex to think a framework will always work for everyone. It won’t. I still think Agile Scrum and Kanban are great tools and what I’ll strive for until something better comes along. However, if people aren’t malleable enough to jump the Agile train, we’ll have to find a compromise because being on different trains isn’t an option.&lt;/p&gt;
&lt;p&gt;Perhaps you can experiment with a &lt;a href=&quot;https://seattleScrum.com/you-wont-change-your-organization-without-an-optimization-goal/&quot;&gt;system optimization goal&lt;/a&gt;. It’s something I intend to give a go soon. Instead of implicitly assuming that the de facto standard framework is the optimum, a system optimization goal helps teams tweak their process towards an undefined optimum by making the right choices along the way.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Try using a &lt;a href=&quot;https://seattlescrum.com/you-wont-change-your-organization-without-an-optimization-goal/&quot;&gt;system optimization goal&lt;/a&gt;. Instead of implicitly assuming that the de facto standard framework is the optimum, a system optimization goal helps teams tweak their process towards an undefined optimum. It gives direction when you’re not sure what path will yield better results. --&amp;gt;&lt;/p&gt;
&lt;p&gt;Alternatively, consider &lt;a href=&quot;https://verraes.net/2014/03/small-controlled-experiments/&quot;&gt;small (uncontrolled) experiments&lt;/a&gt; to iterate on the way you work in which anyone can propose an idea, and you put it to the test, evaluate and either keep the idea or ditch it. They’re &lt;a href=&quot;https://fs.blog/reversible-irreversible-decisions/&quot;&gt;reversible&lt;/a&gt;, after all.&lt;/p&gt;
&lt;p&gt;Do whatever to make it better! Because there’s no use in doing something that doesn’t work.&lt;/p&gt;
</content:encoded><category>industry</category><author>Tim Severien</author></item><item><title>Face Detection on the Web with Face-api.js</title><link>https://web.archive.org/web/20230325234405/https://www.sitepoint.com/face-api-js-face-detection/</link><guid isPermaLink="true">https://web.archive.org/web/20230325234405/https://www.sitepoint.com/face-api-js-face-detection/</guid><pubDate>Mon, 21 Jun 2021 00:00:00 GMT</pubDate><category>js</category><category>tools</category><author>Tim Severien</author></item><item><title>Improving online tech discussions</title><link>https://tsev.dev/posts/2021-05-10-improving-online-tech-discussions/</link><guid isPermaLink="true">https://tsev.dev/posts/2021-05-10-improving-online-tech-discussions/</guid><pubDate>Mon, 10 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last weekend I found myself lying in bed scrolling my highly filtered Twitter timeline. Just before shutting my eyes, I read a tweet in which someone showed support to Sara Soueidan against some internet hate. Being an imperfect human, I decided sleep could wait and spent an hour reading blog posts and tweets to reconstruct the events of that day.&lt;/p&gt;
&lt;h2&gt;What happened?&lt;/h2&gt;
&lt;p&gt;A short recap:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Brian Boyko publishes &lt;a href=&quot;https://dev.to/brianboyko/tailwindcss-adds-complexity-does-nothing-3hpn&quot;&gt;TailwindCSS: Adds complexity, does nothing&lt;/a&gt;, an opinion piece about Tailwind.&lt;/li&gt;
&lt;li&gt;Sara shares the article on Twitter. She later deleted the tweet.&lt;/li&gt;
&lt;li&gt;Adam Wathan, the author of Tailwind, replies with: “Thanks for choosing to use your platform to ruin my day 🥰” (&lt;a href=&quot;https://twitter.com/adamwathan/status/1390663794985250816&quot;&gt;link to tweet&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Observers are upset and harass people.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Some posts followed after the events. Cher Scarlett calls out the toxicity of the Tailwind community in her post &lt;a href=&quot;https://dev.to/cher/sexism-racism-toxic-positivity-and-tailwindcss-cil&quot;&gt;Sexism, Racism, Toxic Positivity, and TailwindCSS&lt;/a&gt;, Hidde de Vries wrote about the value of criticism in his post &lt;a href=&quot;https://hiddedevries.nl/en/blog/2021-05-08-criticism-pushes-the-web-forward&quot;&gt;Criticism pushes the web forward&lt;/a&gt;, and Mykolas Mankevicius replies to the original Tailwind criticism in &lt;a href=&quot;https://dev.to/neophen/tailwind-is-bad-because-i-don-t-like-it-24eh&quot;&gt;Tailwind is bad because I don&apos;t like it&lt;/a&gt;. There are probably more posts and dozens of tweets, but frankly, I’ve read enough.&lt;/p&gt;
&lt;h2&gt;The double standards of online and offline tech discussions&lt;/h2&gt;
&lt;p&gt;I recently explained to a family member that a software engineer rarely just writes code all day. It involves an immense amount of teamwork. We discuss, negotiate, and compromise all the time. Elementary communication and empathy is a necessity that is too often overlooked. Skills we use too little in online discord.&lt;/p&gt;
&lt;p&gt;If we’d treat our team members the same as some folk on Twitter treat their tech peers, we’d either had to sit down with HR and make amends or get fired for toxic behaviour. Why do we tolerate this online?&lt;/p&gt;
&lt;p&gt;Would you tell the hard work of a team member is useless and compare it to a fart? Would you be angry with a teammate that shares an opinionated blog post you disagree with? Would you harass members if you saw any of the above happen? Why do people think such behaviour is justified? Are these people this toxic in real life? If so, why would someone hire them?&lt;/p&gt;
&lt;p&gt;None of it is productive. In fact, much of it is awful and counterproductive. Instead of convincing people to use Tailwind, those who harassed Sara over this probably achieved the opposite.&lt;/p&gt;
&lt;p&gt;I know this problem is greater than the tech community, but we have to start realising that every thought expressed as a tweet came from a human being, and our thoughts are sent back to them.&lt;/p&gt;
&lt;h2&gt;Good discord requires understanding&lt;/h2&gt;
&lt;p&gt;In my experience, few opinions are unfounded, but people take little time to understand them. Tailwind does strike me as inline styles on steroids, but I’d love to hear how it works for people!&lt;/p&gt;
&lt;p&gt;In 2017 I wrote &lt;a href=&quot;https://timseverien.com/posts/2017-08-19-thou-shalt-like-my-tech/&quot;&gt;a (poor quality) blog post about tech discussions&lt;/a&gt;. It’s the same thing all over again: people are harassed by others with a different opinion. Twitter isn’t the best platform to talk about context. If I need to understand why people like Tailwind, I need to understand a great deal of the projects it’s used in and the team(s) that work on them. Odds are people use it to tackle problems I don’t realise I have or might even never face.&lt;/p&gt;
&lt;p&gt;If we write a review or critique piece about a tool, we have to not only thoroughly understand the topic, but also the context in which others use it. We can’t try it for five minutes to confirm our biases for our blog post.&lt;/p&gt;
&lt;p&gt;If we want to educate others, “[tech] is the best and the rest suxxx” or any combination of 180 characters just won’t cut it. We have to explain what problems tools try to solve and in which contexts they could be useful.&lt;/p&gt;
&lt;p&gt;Instead of harassing folk and repeating the same shit a couple of others already tweeted, write or share the kind of blog post that is actually respectful and helpful. Take away the doubts through arguments and example, not by blatantly telling people they’re wrong.&lt;/p&gt;
</content:encoded><category>industry</category><author>Tim Severien</author></item><item><title>What is Vitejs? An Overview of the New Front-end Build Tool</title><link>https://web.archive.org/web/20250201013717/https://www.sitepoint.com/vitejs-front-end-build-tool-introduction/</link><guid isPermaLink="true">https://web.archive.org/web/20250201013717/https://www.sitepoint.com/vitejs-front-end-build-tool-introduction/</guid><pubDate>Mon, 10 May 2021 00:00:00 GMT</pubDate><category>tools</category><category>web</category><author>Tim Severien</author></item></channel></rss>