Web dev at the end of the world, from Hveragerði, Iceland



Free yourself from Node with import maps and test-driven web development
by Baldur Bjarnason

Get the course, and everything else, with the Everything 2023 bundle for $95 USD

Buy the web-book/course for $59 USD

The web is amazing.

Amazingly complex, that is.

The web has become the most fragile part of most software projects, the slice of the stack that is the most likely to break or require costly updates in the long run.

Set up a new project with all that a fresh web project requires—bundling, testing, frameworks–and you’re off to a running start.

Until your coworker tries to clone the repository and gets eight security advisories during npm install on a four-hour-old project.

Another coworker doesn’t even get past the install step. One of the testing dependencies is “opinionated” and won’t install. You’re both using desktop Linux—but their flavour is different enough for install to fail.

A week passes and your webpack config has already become an inscrutable document whose meaning is arcane lore.

Switch work on another project for a few weeks and when try to get it up and running on another machine you discover that…

…It won’t run any more,

…the dependencies all have security advisories listed,

…Typescript suddenly complains about code it used to think was fine,

…the bundler bails with an obscure webpack error,

…and tests are failing,

all happening even though you haven’t touched an actual line of code yet.

These days, there’s no such thing as a small or medium-sized web development project. Everything has the complexity of a major investment of time.

Node is a bad fit for most web development projects. It is an especially bad fit for projects that aren’t using Node for the server in the first place.

Imagine if everybody had to wrangle Ruby or Rust installation and hundreds of arcane gems or crates every time they wanted to add code to a website?

Web Development with Node is a complexity tax on all software development

The biggest pain point in many PHP or Ruby on Rails projects is the front-end tooling.

You know that feeling of dread: starting a new PHP, Ruby, or Python web project and trying to navigate Node, just to be able to work on the front end.

Oh, no. Why do we have all of these security alerts? All we’re using the JS for is to validate some forms! Most of these alerts are for server-side Node libraries. The framework can’t be including them all in the bundle, right?

Why doesn’t the testing framework work any more? It used to work in our other projects.

Crap! The new version of the form-handling library has changed its API. Again. Now I’m juggling three different APIs across four projects even though I’m using the same library in all of them.

Damn. There’s a new version of Typescript out. Now I need to figure out what they’ve changed.

Most of us have stories and none of them are of the happy “they all lived happily ever after” kind.

It’s a pain, but node, bundling, and the rest is just a hard requirement for modern web development, right?


It doesn’t have to be this way.

What if it were painless?

Imagine starting a new server-side project…

You don’t need to install a Node-based test runner—just a couple of text files and you can run the tests in genuine browsers.

No matter how much time passes, working tests will only fail if you actually change something.

You have manageable processes for your dependencies that are tied into your testing. Even when something does break, you almost always know exactly where.

You only bundle the code for deployment if your testing shows the project needs it for loading speed.

No additional tooling is needed. Just what comes with your Operating System, shell, and your Ruby, PHP, or Python framework. HTML is our test runner and any framework that works with HTML can play.

There is a little bit of Node, but it’s contained entirely in a Google Action and exists only to give the action the same capabilities you have during development.

Web development can be much less complicated.

This isn’t a fantasy

Web servers and browsers are amazing tools in their own right. One feature in particular is about to change the way many of us code for the web.

Import Maps.

Import maps give you many of the conveniences you expect from node package management and bundling, but without the complexity and overhead that comes with Node and npm.

Where they shine in particular is in testing, where their ability to remap module paths on the fly simplify many tasks, from mocking to dependency management.

Import maps will be the new default for Ruby on Rails, are supported in Symfony using the experimental AssetMapper component, and they are a great match for web development projects that aren’t built with Node, whether they be in Python, PHP, Ruby, Rust, or Go.

What Uncluttered offers you

Uncluttered is a 20 000 word text-oriented course that:

  • Will guide through setting up a web development project that combines everything you need for modern web development, but without locking you in – or out of – other approaches in the future.
  • It includes a link to a GitHub project template I’ve made, that you can use to get up and running.
  • Provides a thorough introduction to Import Maps and how they can be used to help you with testing, dependency management, and structuring your project.
  • Introduces you to test-driven development as it applies to the web including an overview of how you can apply the approach to solve concrete problems in your web development workflow.
  • Outlines several import map strategies you can use to prevent dependency hell.
  • In addition to a web course that’s hosted by Teachable, you can also download an EPUB or PDF version of the course for offline reading.

Buy the web-book/course for $59 USD

Mocha for testing in the browser and out

Mocha is one of the oldest testing frameworks in the JavaScript ecosystem. It effectively works everywhere. The node Command-Line version is one of the more popular testing frameworks in the node space.

It’s directly used or supported by many common testing tools in web development such as Cypress or @web/test-runner.

This means that if, for any reason, the browser-based setup becomes too limiting for your project, there are a number of migration strategies for you that you can use without rewriting a single test.

How to set your browsers up for reliable testing

Browsers are designed to be end-user software whose settings and state is tailored to each individual. Reliable web software testing requires predictable state.

Those two aren’t entirely compatible. The course has a lesson that talks you through approaches towards session management in the big browsers and offers potential automated solutions.

Test-driven development without the religious fanaticism

TDD is a solution. It fixes certain problems. It will not work for everything.

But, it really does work for very many problems.

The course will introduce you to the basic concepts behind unit, integrated, integration, and end-to-end testing – I even have a go at explaining why the terminology is so confusing. I go over mocking or test doubling – how those two terms represent two different approaches in testing and which one I recommend and why.

Dependency management from the future

Until import maps were introduced, we only had one realistic option for dependencies in web development:

Bundle them all together into statically linked files like an overworked Unix developer from the 1970s.

Bundling is archaic. It works well for programming languages like Go or Rust that have been designed for it from the ground up, but JavaScript – for better or for worse – is of a different lineage.

JS is descended from the Smalltalks, Schemes, and Common LISPs of history. Languages that are dynamic at every level and in every way and for whom static binary deployment means taking away much of what make them special.

This dynamism may be a disadvantage for certain problems, but this is what JavaScript is.

Pretending that it’s a statically typed language in the style of C or C++ will only lead to defective software and miserable developers.

In true dynamic language fashion, Import Maps are a new primitive in web development. It lets you dynamically, conditionally, and on-demand remap any JS module to another.

It’s the building block of future dependency management solutions.

And instead of being a hard requirement for deployment, bundlers become a performance optimisation – something you can introduce granularly to solve a specific problem you know you have, and whose complexity you can otherwise avoid.

The Course

Cover for the 'Uncluttered' web course

Uncluttered: free yourself from Node with import maps and test-driven web development is a web-based course that will guide you through test-driven web development using just the web browser, a web server, some HTML, and some JS modules that run natively in the browser without building.

Instead of the usual video course or thick book, it’s structured as a series of bite-sized written lessons, averaging at around 500 words or 2–4 minutes of reading.

Some of the lessons show you one of the steps in setting up a Node-free development environment.

Others are examples of how test-driven development would work in practice. A few of them are context—nuggets of advice based on years of web development experience.

It’s an even mix of practice and context. Some lessons show you how to do a thing. Others present food for thought.

It has plenty of code examples throughout and shows you how import maps give you web development superpowers.

Import maps are the best thing to happen to test-driven web development in years.

You can drop in and read a short lesson in between the many other things we all have going in our lives.

What matters is what works for your, for your project, for your workplace.

Get the course, and everything else, with the Everything 2023 bundle for $95 USD

Buy the web-book/course for $59 USD

Who is Baldur Bjarnason?

A square headshot of Baldur Bjarnason I’ve been making websites for over twenty-five years, working as a web developer (front-end, back-end, or full-stack) for both companies and charities, small and large. I’ve written two books on topics related to software development and have been writing about web development online for years.

In the past I’ve worked with Node, Ruby, Python, and PHP projects, so I have first-hand experience with the disconnect that can exist between Node-based front ends and servers written in other languages. I’ve even had the misfortune of once working on an ecommerce site whose core components were written in C and then wrapped in PERL.

It wasn’t pretty.

I’ve seen things and my goal with my books and courses is to make sure you never have to.

Lesson plan/chapters


  1. WTF is test-driven development?
  2. You’re going to need a web server
  3. Loosely coupled systems
  4. Setting up the project
  5. Set up the web server (don’t just curl|sh random scripts)
  6. Don’t over-engineer basic setup
  7. The browsers are our runtime
  8. The browsers are our primary web development tool
  9. EXAMPLE: browser launch scripts
  10. EXAMPLE: the mocha HTML file
  11. Continuous Integration (CI)


  1. On code smell and gut feeling
  2. JavaScript modules are great, but also a little bit broken
  3. The troubled history of standard JavaScript modules
  4. We can do better
  5. Managing dependencies in code is bad
  6. Import maps bring loose coupling to ES modules and it’s genuine magic
  7. We can use import maps right now, today
  8. A new primitive for decoupling JavaScript code


  1. Planning
  2. Where to begin?
  3. EXAMPLE: the actual test
  4. And we’re d—oh, wait, there’s more?
  5. Classicist testing
  6. Mockist testing
  7. Which school to choose?
  8. EXAMPLE: mocking sources of randomness


  1. We have to talk about dependencies
  2. Consider avoiding external dependencies altogether
  3. Implicit dependency testing
  4. EXAMPLE: the Mockist approach
  5. Update and test one module at a time
  6. Split your dependencies into layers


  1. Deployment strategies for import maps
  2. If your platform has a strategy, use that
  3. Progressive Enhancement
  4. Polyfill and commit
  5. Bundle using esbuild and a plugin
  6. Which should you choose?
  7. Then what happens?

Buy the web-book/course for $59 USD

You can also find me on Mastodon and Bluesky