RSSAmplifier

Blog

Markus Oberlehner

Markus Oberlehner is a Software Engineer living in Austria. He writes articles about Vue.js / Nuxt, React / Next.js, Test-driven Development, and CSS.

markus.oberlehner.netRSS feed ↗260 posts

Latest posts

Code Reviews Will (and Have to) Die!

Reviewing code has become dramatically more expensive relative to the cost of writing code. In the past, code reviews were relatively cheap, or maybe even paid for themselves in the long run. But things have changed: agents now build features in minutes, not days, while human reviewing time stays the same and its benefits diminish...

What We Do

Is it worth automating things? I'm a software developer. My job is to automate things. Automation, over the last couple of hundred years, has made many of us rich (compared to our ancestors). So the answer should be simple, and yet it isn't...

Escaping the Apple Ecosystem - My Journey to Digital Sovereignty

There are plenty of good reasons to turn your back on the major US technology companies: from shady monopolistic business practices (e.g., app stores); to crafting ever more addictive algorithms that tie us to our screens; to enabling state mass surveillance. Yet all of this is not new. Companies like Apple have been trying to force us into their golden cage of services...

Visual Regression Testing With Vitest

Visual regression testing is the perfect tool to catch regressions within our UI component libraries and applications early, yet few teams practice it. Teams often avoid it due to complicated setups, cumbersome workflows, and difficulties syncing tests across different operating systems...

Let's Share Our Failures Not Our Successes!

Stop sharing only the things that went well! Start sharing your failures instead! I wholeheartedly agree with David Duchovny that there is more to learn from failure than from success. So why are we so obsessed with sharing mostly useless (and often fake) success stories...

Understanding 'Magic' in Programming Frameworks: Examples, Pros and Cons

What do we mean when we talk about "magic" in programming? I recently came up with a simple definition while watching Ricky Gervais explain the difference between believing in science and believing in religion...

Will LLMs Ever Be Creative Enough To Solve Programming?

Imagine you train a toddler to read, mixing in texts about physics and mathematics. Sure, at some point, the toddler might be able to predict the correct formula when you ask them to solve a particular mathematical problem, but they don't have the slightest idea why this is the correct answer...

Nomenclature in Microservices Architecture: Modules, Components, and Services

'Naming things is hard!' is a common saying in developer circles. One thing that has confused me more than once is how to differentiate between modules, components, and services in the context of microservices. But recently, I watched a talk by Ian Cooper, where he clarifies what is what and how these three terms relate to each other...

Parallelizing Specmatic Contract Tests with Playwright

Specmatic quickly became my favorite tool for contract testing microservices and for using it as a stub service to test frontend applications. Together with Playwright this makes for a great combo! Yet there is a problem with this approach: Out of the box we can't run tests in parallel with this setup because Specmatic's stub server, by default, isn't designed for parallel execution...

You Can't Fight 'Not-enoughness' with 'More'

After being rejected to speak at several conferences, I wondered: Why is it so important to me? The answer: a deep sense of not-enoughness that I try to overcome by pushing harder and harder to accomplish more and more...

Using react-hook-form with React 19, useActionState, and Next.js 15 App Router

I'm currently working on building a comprehensive form component with React. As usual, as I typically do, I built it all by myself without reaching for a form library. And, as usual, although I kinda got where I wanted, I was not satisfied with my code and decided to opt for a ready-made form library...

Non-English Variable, Function, and Symbol Names in Code

Recently, I switched jobs from karriere.at, Austria's largest job platform, to the Austrian Federal Computing Centre, where my colleagues and I work on modernizing Austria's digital tax services. One of the first things I noticed, looking at the new codebase, was that we heavily use German terms for naming certain classes, functions, and variables. At first, this seemed to me like a giant red…

Running Visual Regression Tests with Storybook and Playwright for Free

Storybook’s visual regression testing tool Chromatic is powerful but expensive. Luckily, with tools like Playwright, we can build a free alternative for running visual regression tests on our UI components. In this article, we’ll explore how to integrate Playwright with Storybook and use Docker to ensure consistent test results across different machines and CI environments.

Running Next.js with Docker

Nowadays, there are plenty of ways to run and deploy our Next.js application to the World Wide Web. Yet, especially in an enterprise environment or when we don't want to depend on a particular provider like Vercel, we may consider bundling our Next.js-powered application within a Docker container...

The Microservices and Microfrontend Mindset: Think Decoupled

Although microservices and microfrontends have been popular choices for building modern software systems, many projects fail to reap their promised benefits. In this article, we explore why this happens and how embracing a decoupled mindset can help you get the most out of these architectures.

Microservices, the Expulsion From E2E Testing Paradise, and the Path to Salvation: Contract Testing

Recently, I was back on the job hunt. At one particular interview, my potential future colleagues and I got into a discussion about testing, specifically the term end-to-end testing. I was in the awkward position that I didn't have a word to describe a type of test where you run your web application in a real browser...

Using Testing Library jest-dom with Vitest

Vite brought super fast bundling and hot reloading to our regular dev workflow, and Vitest is doing the same for our testing workflow. So I'm mostly transitioning away from Jest in favor of Vitest. But there are a lot of amazing tools in the Jest ecosystem, and not of them can be easily replaced. Luckily, the Vitest API is mostly compatible with the Jest API...

AI-Enhanced Development: Building Successful Applications with the Support of LLMs

In this article, we'll explore the integration of AI-assisted development with established best practices. We'll discuss how to leverage techniques like TDD, writing user stories, and setting acceptance criteria in the post-ChatGPT era, addressing the limitations and opportunities presented by LLMs...

No More Mocking! Write Better Tests for Microservices-powered Server-side Rendered Applications with Contract Tests

I could never quite wrap my head around how to test SSR applications (Nuxt, Next.js, Laravel, etc.) that talk to HTTP APIs (e.g., microservices). Using the built-in mocking capabilities of tools like Playwright and Cypress is not possible in this scenario because...

Nuxt: Building a Similar Products Feature with Vector Search and SQLite

In this article, we’ll explore using SQLite with the VSS plugin to build a “similar products” functionality with Nuxt, Vectory Search, and SQLite...

Your Own Vector Search in 5 Minutes with SQLite, OpenAI Embeddings, and Node.js

One thing that is often overlooked in the current AI chatbot hype is another use case for Large Language Models (LLMs): Vector Search with text embeddings...

Is Returning Composables from Composables an Anti-Pattern in Vue Applications?

This article might not meet your expectations if you're looking for a black-and-white answer to whether returning composables from other composables in Vue is an anti-pattern. Instead, I aim to explore this concept and share my perspective...

Reactive Data Fetching and Updating in Nuxt 3: Automatically Refresh `useFetch()` When Deleting or Updating Data

Managing data across different components is a common challenge when working with modern web frameworks. Imagine a typical scenario where actions (e.g., deleting or updating data) in one component must reflect changes in another—for example, fetching a list of items, like products, and seamlessly reflecting updates like deletions across the app...

Test Smarter, Not Harder: Focus on Outcomes, Not Outputs

When we dive into the world of testing in programming, it's easy to get caught up in the numbers game—how many tests have we written? What's our code coverage? However, if we pause and consider the bigger picture, we realize that the true measure of success isn't the output it's the outcome...

Embracing Simplicity: The Rule of Least Power

When it comes to coding, sometimes less is more. This concept is encapsulated in The Rule of Least Power, a principle suggesting that for any given task, we should reach for the least powerful language or tool that gets the job done...

Building a Chatbot with Next.js Running LLaMA 2 Locally

LLaMA 2, a fresh Open Source language model by meta, is a powerful tool for natural language processing tasks. In this guide, we'll build a chatbot using LLaMA 2 and Next.js, the popular React framework...

Running Nuxt 3 in a Docker Container

In this article, we'll dive into the world of Nuxt 3 and Docker, exploring how they can work together to streamline our development and deployment processes. We'll walk through setting up a Nuxt 3 application in a Docker environment for production and development...

Leveraging Docker to Run CLI Tools in Languages Like Java, Ruby, and PHP Without Local Installation

We often find ourselves needing to use various command-line interface (CLI) tools written in different programming languages. Sometimes, we don't have the required programming environment set up on our local machines or may not want to install it for various reasons. Our hesitance could be due to concerns about system clutter, potential conflicts with other software, or simply wanting to maintain…

Beware of Leaky Abstractions When Relying on Attribute Inheritance in Vue Components

In this article, we will discuss the problems associated with attribute inheritance, also known as fallthrough attributes, in Vue components and why it might be best to avoid using them. Attribute inheritance can increase the API surface of components, making it challenging to maintain and refactor them in the future...

The Best of Both Worlds: Progressive Enhanced Hybrid Applications

In the ever-evolving landscape of web development, we have seen a shift from traditional Multi-Page Applications (MPAs) to modern Single Page Applications (SPAs), each with advantages and drawbacks. To deal with those drawbacks, frameworks embracing the principles of, what I call, Progressive Enhanced Hybrid Applications (PEHAs) emerge as a new approach that addresses the individual challenges...

Building a ChatGPT Client with Remix: Leveraging Response Streaming for a Chat-like Experience

ChatGPT has recently gained significant attention due to its powerful natural language understanding and generation capabilities. While the official ChatGPT client offers a decent UX, I wanted to create a better client as a pet project. In this article, I'll share insights on integrating OpenAI response streaming with Remix, resulting in a ChatGPT-like experience...

Using Error Boundaries in Remix to Prevent Broken Pages on Client-Side-Only Errors

Error handling is essential when developing web applications to let users know why and what is going wrong and ensure a smooth user experience. In React applications, error boundaries provide an excellent way to catch errors and display fallback UIs when something goes wrong...

Building a ChatGPT Client with Nuxt 3: Leveraging Response Streaming for a Chat-like Experience

ChatGPT has recently gained significant attention due to its powerful natural language understanding and generation capabilities. While the official ChatGPT client offers a decent UX, I wanted to create a better client as a pet project. In this article, I'll share insights on integrating OpenAI response streaming with Nuxt, resulting in a ChatGPT-like experience...

Utilize TypeScript's Omit & Partial for Data Creation & Update Scenarios

Dive into the world of TypeScript's powerful utilities, Omit and Partial, as we explore their potential in data creation and updates. Learn how to harness their power to streamline your code and elevate your TypeScript game...

Harnessing ChatGPT as Your Writing Assistant: A Step-by-Step Guide

Are you seeking a trusty sidekick to help you tackle your writing projects? Enter ChatGPT, an AI-powered language model that can give you a helping hand! As a powerful writing assistant, ChatGPT can support you in creating engaging content for your technical audience. But, like any trusty sidekick, it's essential to understand its strengths and limitations to maximize its capabilities...

The Three-Layer UI Component Architecture: Versatile Building Blocks for Crafting Multiple Design Systems

Creating custom component libraries for multiple products or applications can be challenging, particularly for companies that need to maintain two or more different design systems. The Three Layer UI Component Architecture is an approach for maintaining reusable components we can use as building blocks for multiple design systems...

How To Fix Spacing Between Text Blocks When Using Tailwind CSS

Did you ever encounter the problem that the space between a text block and some other element does not seem right? For example, although we've added the same `mt-4` to a text block and some images, the space between text and image looks much larger than the space between two images. So instead of `mt-4` we try `mt-3`, but it just doesn't work out. This is because the same margin can look visually…

Using Mock Service Worker with Vitest and fetch

The JavaScript ecosystem has a lot to offer when it comes to testing. With the recent addition of Vitest we get the performance and convenience features of Vite for testing too. Mock Service Worker is an excellent mocking solution for mocking network requests in the browser and Node.js...

Refactoring a House: Software Architecture is Architecture After All

Now and then, fierce debates ignite on Twitter about whether the term Software Architecture is misleading or even harmful. The argument goes as follows...

TODO Timeboxing

Every couple of months, I reach a point where I have to declare TODO bankruptcy. The items on my TODO list are getting more and more, and I schedule more and more todos each day to get ahead. But obviously, that doesn't work—quite the opposite...

Manual testing, E2E testing, unit testing – how to decide which testing strategy to use?

When we first start to dip our toes into the deep waters of automated software testing, all those different kinds of tests can feel intimidating. In this article, I give you a quick overview of the most crucial testing strategies. After reading this tutorial, you should have a much clearer picture of the strengths and weaknesses of the different kinds of testing methodologies...

How to Clean Up Global Event Listeners, Intervals, and Third-party Libraries in Vue Components

Ideally, Vue components are self-contained pieces of UI without any observable side effects to matters outside the component's scope. But unfortunately, that's not always possible...

Multiple Root Nodes and Attribute Inheritance in Vue 3

A tweet by Manuel Matuzović reminded me that in Vue 3, we finally have fragments. But I also remembered that this is not without its problems...

Vue Project Directory Structure: Keep It Flat or Group by Domain

When starting a new project or refactoring an existing one, the question often arises: how to set up the project's directory structure. My first advice is to keep the folder hierarchy as flat as possible for as long as possible. But depending on the size of our project, there might be a time where we feel the need to add additional folders to organize our Vue components and other files...

Vue Composition API: VueUse Composable Library

The Composition API makes it very easy to share code between components in our codebase or even across projects via npm packages. I recently stumbled upon the excellent VueUse library that provides us with a ton of valuable composables. In this article, we take a closer look at some of the most interesting ones...

Maintaining Shared Code and the Inversion of Responsibility Principle

Over the past few weeks, I've been thinking a lot about effectively maintaining shared code within an organization. For example, think of a private npm package that multiple teams depend on. How can we ensure that changes to shared code made by Team A don't break Team B and C's code...

Vue Composition API: Composables

What makes the Vue 3 Composition API so much better than the Options API is code sharing. Inside the setup hook of a component, we can group parts of our code by logical concern. We then can extract pieces of reactive logic and share the code with other components...

Don't Write Documentation!

Whenever we find a severe bug that is caused by an error in a complicated piece of code, in my experience, the first instinct is that better documentation is needed. So that the next time we need to debug the code, at least we know what it is supposed to do. I argue that this is a waste of time...

Cypress Live-Reload Tests on Code Changes

Out of the box, Cypress offers an amazing live-reloading feature. But there is one caveat: live-reloading only works when changing test code, not when updating the application code. Nowadays, we are used to live-reloading in the browser thanks to webpack hot module replacement (HMR) and other fantastic development tools like Vite or Snowpack. If we had something similar in Cypress, practicing TDD…

Decoupling Component Tests From Implementation Details with Preconditions

When testing components (e.g., Vue or React) or regular JavaScript modules, we typically want to decouple our test code from the implementation as much as possible. Ideally, we want to write black box tests. That means that we are only allowed to interact with the public API of the component under test...