How to fix "Could not resolve any esbuild targets" when building assets

I recently encountered this error as I was trying to build assets:

$ node esbuild.config.js
.../node_modules/esbuild-plugin-browserslist/dist/resolveToEsbuildTarget.js:43
        throw new Error('Could not resolve any esbuild targets');
        ^

Error: Could not resolve any esbuild targets
    at resolveToEsbuildTarget (.../node_modules/esbuild-plugin-browserslist/dist/resolveToEsbuildTarget.js:43:15)
    at Object.resolveToEsbuildTarget (.../node_modules/esbuild-plugin-browserslist/dist/index.js:9:64)
    at Object.<anonymous> (.../e...

Sending newsletters via rapidmail with SMTP and one-click unsubscribe

If you need to implement newsletter sending, rapidmail is a solid option.

Support is very fast, friendly and helpful, and the initial setup is simple. Since rapidmail works via SMTP, you can simply ask the Ops team to configure SMTP credentials for your application.

You also do not need to use rapidmail’s built-in newsletter feature. Instead, you can send emails as transactional mails, which allows you to keep the entire newsletter logic inside your application.

One thing to keep an ey...

Postgres Ranges

Postgres supports multiple built-in range datatypes:

  • int4range
  • int8range
  • numrange
  • tsrange (range with timestamp without timezone)
  • tstzrange (range with timestamp with timezone)
  • daterange

They represent a start and endpoint of something in a single column. Image you're building a vacation booking feature:

create_table :events do |t|
  t.date :starts_on
  t.date :ends_on
end

This is how you would use a range type in a migration:

create_table :vacations do |t|
  t.daterange :period
end

See [t...

TypeScript: Get in Shape with Structural Typing

TypeScript basically uses structural typing, which is conceptually quite similar to duck typing, but with static compile-time type checking. We'll explore what this means in practice.

TypeScript is a superset of JavaScript, meaning TypeScript compiles down to native JavaScript syntax and checks type consistency only at compile time.

Idea of Structural Typing

TypeScript only wants to know whether the shapes of two objects are identical:

interface Point2D {
  x: number;
  y: number;
}

function printPoint(p: Point2D) {
  c...

ActiveRecord: Cleaning up your database with ignored_colums

Leaving old unused DB columns around after a migration is confusing for other developers. However, dropping columns too eagerly might also cause problems and extra work. If you want to mark columns for future deletion, but you are unsure, whether you can simply drop them right now, use these tools:

Add a comment to your DB schema

With schema comments you can add a comment like LEGACY as of yyyy-mm-dd to your DB schema.

Ignore the column

With [...

Bash functions to provide repository context for LLM chats

I use the Gemini web chat interface quite extensively. One thing that is tedious is giving it all the context it needs to do a proper job. Context engineering is not an easy task, but on the other hand we now have context limits of ~1 million token, which allows us to just dump in everything we have in many cases. And when we do that in the web interface, we can avoid extra costs that would be charged when using the API!

The functions below pack your current work (diffs, full repos, or specific commits) into XML/Diff files, which are then ...

How to accept more than 4k query parameters in Rails

I have a form with a dynamic number of fields. Submitting it worked fine until I tried out a very large version of it. The development log was not very helpful:

Invalid or incomplete POST params

As it turned out, the following exception did not reach the log output

Rack::QueryParser::QueryLimitError
Error Message: total number of query parameters (6313) exceeds limit (4096)

If you ever happen to be in the same position, this is how to increase the limit of allowed query parameters:

# config/initializers/rack_query_parser.rb
...

Testing ActiveJob `limits_concurrency` with Solid Queue

The :test adapter doesn't respect limits_concurrency configuration. Switch to :solid_queue adapter in your test to verify blocking behavior.

Job Configuration

class MembershipJob < ApplicationJob
  limits_concurrency(key: ->(membership) { membership })
end

The problem

When using the default test mode for enqueuing jobs, both will be enqueued immediately. However, we actually we want to test that only one of both will be enqueued as the other should have been blocked.

# This doesn't actually test concurren...

Debugging Rails Active Jobs with the Vanilla Adapters

Short reference on how to quickly debug the vanilla Rails job adapters.

Queue Adapters by Environment

Environment Adapter Jobs Run In Worker Needed?
development :async Rails server process No
test :test Not executed (stored) No
production :solid_queue Separate worker Yes (bin/jobs)

Development (:async)

Jobs run in background threads ([Concurrent Ruby ThreadPoolExecutor](https://ruby-concurrency.github.io/concurrent-ruby/maste...

How to iterate over all ActiveRecord models

Sometimes you have a maintenance script where you want to iterate over all ActiveRecord models. Rails provides this out of the box:

# script/maintenance_task.rb

# Load all models eagerly, otherwise you might only get a subset
Rails.application.eager_load!

ApplicationRecord.descendants.select(&:table_exists?).each do |model|
  # ...
end

Caution

If you iterate over individual records, please provide a progress indicator: See [https://makandracards.com/makandra/625369-upload-run-scripts-production](https://makandracards.com/...

Rails: Join model table migration template

When creating a database table for a join model without further importance, you can use Rails' create_join_table:

class CreateSchoolsStudents < ActiveRecord::Migration[7.2]
  def change
    create_join_table :schools, :students, column_options: { foreign_key: true } do |t|
      # t.timestamps # Optional
      t.index [:student_id, :school_id], unique: true
    end
  end
end

This will create a table without an id column and without timestamps. It will have school_id and student_id columns with null: false constraints ...

Working with lists of DOM elements in JavaScript

When you query the browser for DOM elements, there are some footguns you should know about.

Some lists are synchronized with the DOM

Some DOM APIs return live lists that automagically update their contents as the underlying DOM is manipulated.

Example

Let's assume we have two <div> elements:

<div id="one"></div>
<div id="two"></div>

We have multiple ways to retrieve a list of these elements. At first glance, they all look the same:

let liveList = element.getElementsByTagName('div')
let nonLiveList = docum...

Faux-disabled fields in HTML forms

You want to prevent input to a form field, but all the solutions have side effects:

  • The [readonly] attribute is only available for text fields, but not for checkboxes, selects, buttons, etc.
  • The [disabled] attribute is available for all kinds of fields, but will no longer include the field value in the submitted form data.
  • pointer-events: none still allows keyboard input, and does not indicate disabledness visually, or to screen readers.

Ye...

Rails: Adding a unique constraint for a has_one association might be a good default

Most of the time, it's a good default to add a unique index on the foreign key when using Rails’ has_one relationship. This ensures the database enforces the 1:1 constraint and raises an error if your application logic ever violates it.

class User < ApplicationRecord
  has_one :session, dependent: :destroy
end

class Session < ApplicationRecord
  belongs_to :user
end
create_table :users do |t|
  t.timestamps
end

create_table :sessions do |t|
  t.references :user, null: false, foreign_key: true, index: { unique: true ...

Sentry Local Logging in Ruby

Enable local logging for Sentry when:

  • Debugging Sentry event capture locally
  • Testing error handling without polluting production metrics
  • Developing background jobs and want to see what Sentry captures

How to enable

To capture and log Sentry events locally during development without sending to the server, add this to config/initializers/sentry.rb inside the Sentry.init block:

if Rails.env.development?
  # Use dummy transport to prevent actual transmission to Sentry
  config.transport.transport_class = Sentry::DummyTran...

Using a virtual column for trigram indexes in PostgreSQL

Full-text search can reach its limits in terms of flexibility and performance. In such cases, trigram indexes (pg_trgm) offer a lightweight alternative.

You can base the index on a virtual column that combines multiple text attributes. A stored virtual column stores the result of an expression as if it were a real column. It is automatically updated when the source columns change and can be indexed like normal data. This keeps your query logic consistent and avoids repeating string concatenation in every search.

def searc...

Unpoly: Passing Data to Compilers

Quick reference for passing data from Rails to JavaScript via Unpoly compilers.

Haml Attribute Syntax

# Ising hash rockets and string symbols (method calls)
= form.text_field :name, 'date-picker': true

# Curly braces or brackets (elements) 
%div.container{ id: 'main', 'data-value': '123' }

Simple Values: data-* Attributes

Use for: Scalar values (IDs, strings, booleans)

%span.user{ 'data-age': '18', 'data-first-name': 'Bob' }
up.compiler('.user', (element, data) => {
  console.log(...

Unpoly: Compiler Selector Patterns

Quick guide for frequently used compiler selector patterns of Unpoly.

1. BEM Component Pattern

When: Reusable UI components with multiple child elements

Examples: toggleable.js, collapsible.js, searchable_select.js

up.compiler('.toggleable', (toggleable) => {
  const checkbox = toggleable.querySelector('.toggleable--checkbox')
  const content = toggleable.querySelector('.toggleable--content')
  // ...
})
%td.toggleable.-inverted.-ignore-when-not-empty
  .toggleable--content
    = form.text_fie...

SQL: Fast threshold counts with LIMIT

Performing COUNT(*) on large tables is slow. Sometimes you don’t need the exact number once results exceed a certain threshold.

For example, you may only need to display 100+ in the UI. Using a plain COUNT(*) would scan all matching rows.

The following query would be pretty slow when counting many rows because it has to scan all rows.

SELECT COUNT(*) FROM movies;

Limiting within a subquery

Use a subquery with LIMIT to cap the scan early. ...

Rails 8: The db:migrate task might not run all migrations in db/migrate/ anymore

In Rails 8 the behavior of the rails db:migrate command has changed for fresh databases (see PR #52830).

  • Before Rails 8: The command runs all migrations in the folder db/migrate/*
  • After Rails 8: The command loads the schema file (db/schema.rb or db/structure.sql) if existing and runs all pending migrations in the folder db/migrate/* afterwards

This speeds up the command. But e.g. migrations with data manipulations are missing.

The only way to run all pending mig...

GoodJob: Ensure you get notified about background exceptions

GoodJob and ActiveJob rescue exceptions internally, preventing exception_notification from triggering. This can cause silent job failures.To get notified, subscribe to ActiveJob events and configure GoodJob's on_thread_error hook. This lets you manually call your exception notifier for every retry, discard, or internal GoodJob error.

# config/initializers/good_job.rb

# Manually notify on job failures, as they are handled internally by ActiveJob/GoodJob.
ActiveSupport::Noti...

Format your JavaScript with prettier

prettier calls itself an opinionated code formatter. I recommend using it for your JavaScript and TypeScript code.

prettier only concerns itself with the formatting of your JavaScript (and also some other file types like json or yaml). When you use it, it has an opinion on every single whitespace and linebreak, as well as a few other things. You renamed a variable and now your line is a bit longer than looks good? prettier will reformat your code.

This might not work for you if you have strong opinions yourself....

Ruby: Avoiding errors when casting user input to Integers

There's a method Integer() defined on Kernel, that typecasts everything into an Integer.

Integer("2")  # 2
Integer("foo")  # Invalid value for Integer() (ArgumentError)
Integer(nil) # Can't convert nil to Integer (TypeError)
Integer([]) # Can't convert Array into Integer (TypeError)
Integer(Object.new) # Can't convert Object into Integer (TypeError)
Integer(2) # 2
Integer("11", 2) # 3

This is very similar but not identical to to_i:

"2".to_i # 2
"foo".to_i #...

Rails: Redirecting the Logger output temporary aka showing Rails logs in the console

Most of the time, when you are interested in any log output,

  • you see the logs directly on your console
  • or you tail / grep some logfile in a separate terminal window

In rare cases it's helpful to redirect the Logger output temporary to e.g. STDOUT.

Rails.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger = Logger.new(STDOUT)

User.save!
#=> D, [2025-09-08T11:12:26.683106 #1094157] DEBUG -- :   User Load (1.1ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]

Many frameworks in Rails ...