RSSAmplifier

Blog

Thomas Schoffelen

schof.coRSS feed ↗50 posts

Latest posts

Turning a Product Into a Platform

I'm a massive fan of building not just a product, but a platform - not only solving a specific product for a user, but building a foundation to allow others to solve their own problems using your system. To do so, I think there are three essential components needed: well-defined constructs, open integration surfaces and community. Well-defined constructs If you want your platform to be something…

Why the Same Test on Paper or Digitally Leads to Different Scores

If you give the exact same test to two comparable classes of students, once digitally and once on paper, it's likely you'll see measurably different results. This has nothing to do with what the students know. It's a property of the medium itself. This phenomenon is called the Mode effect . The mode effect is the systematic difference in scores caused purely by how a test is delivered - paper,…

Notify on New React App Version Available

Sometimes, you want to make sure you show a UI element to let the user know a new version of the front-app of your application is available. Prompting the user to reload the page to get the new front-end bundle is important, as your app's back-end might have changed to something that isn't compatible with an old version of the front-end. Detecting changes To detect an update, the usual pattern is…

Using Worker Threads in Node.js

I had a use case recently where I was building a custom framework for managing background jobs. It needed to be possible for the user to cancel a long-running background job, which should cancel its execution. In Node, that's actually pretty hard to do. If you have a bunch of promises running that are doing things like HTTP requests, there are only very minimal options available to cancel these.…

Focussing on Longevity

One of the things I really enjoy about the co-founders I've currently surrounded myself with, is our shared goal of longevity. We're not trying to build businesses that have hockeystick growth projections, or stressing about month-on-month revenue growth. We're focussing on building what we believe in, and attracting people that share those beliefs to use our products. I believe very strongly that…

Conditionally Loading Native Modules in Expo

I used to be a 'plain' React Native guy, but I've really grown to enjoy using Expo for the last few apps I've built. Much less compile errors to deal with, and I really love using the Expo Go app to quickly debug an app without having to wait for a development build to finish or opening Xcode. Of course, some apps need custom packages with native functionality that isn't available in Expo Go.…

A Short Introduction to QTI

QTI stands for Question and Test Interoperability , and is a standard for exchanging assessment content (and results) between systems. It has developed over the last few decades into one of the most complex, wonderful standards we have in education tech. It's one of those standards though that is really awesome, but hard to get a basic understanding of, without spending lots of time reading all of…

Reviewing the Year's Daily Notes

My daily notes in Obsidian are the cornerstone of my note taking and journaling. I don't really review old daily notes too often, but once a year, in December, I'll look back at the past year's notes day-by-day and write a little overview of the important events and things I learned in that past year. It's a great way to reflect on what the year has brought me, and remind myself of all of the…

Implementing the Repository Model with DynamoDB and Turbine

When you're building a new product at a small startup, speed and flexibility are of the essence. It's important to recognise which architectural patterns make sense, and which ones should be reserved until you have a better understand of the product you're building. Introducing the Repository pattern One pattern that I found is essential to implement from the start is the Repository pattern from…

Copy Rich Text to the Clipboard in JavaScript

I really like how in Linear , if you press Cmd + C , which is listed as 'Copy issue title', it will copy the title as plain text, but also as an HTML link, so that if you paste it in a Google Doc or in Obsidian, it will show a link to that issue. I recently wanted to implement the same thing, but in the reverse, for Street Art Cities, where you can click 'Copy map link' to get a link to the map…

Premature Abstraction of Entity Names

When I started building the current iteration of the Street Art Cities platform a few years ago, I decided on the following core entities: Site Marker A Site can have multiple Markers , and a Marker is connected to a single Site . Both of these are somewhat abstract entities. A Marker has a type attribute, which can be either artwork or place . Places are things that aren’t artworks, but still…

Building a Custom Rate Limiter for Hono

There's a bunch of cool rate limiting packages out there for the Hono web framework, but I wanted something simple that worked for my specific use case, where I want to rate limit per user. To get started, let's set up a new middleware: import { createMiddleware } from "hono/factory" ; export const rateLimitUser = ( callsPerMinute : number) => { return createMiddleware <{ Variables : { userId ?:…

A Story of a Story of Recursion

When I was a child, one time my dad told me a story about a group of robbers sitting under a blade of grass. The story went something like this: In the depth of night, seven robbers sat hidden in the shadow of a blade of grass. The leader of the group stood up and said "Pedro, tell us a story." Pedro stood up and started speaking: "In the depth of night, seven robbers sat hidden in the shadow of a…

Creating Modern Wordpress Themes

I started out as a developer building Wordpress websites when I was in my early teens. Since then, I haven't really touched Wordpress for a while, other than occasionally having to make some small tweaks to one of those ancient websites. Modern Wordpress is something completely different, though. I had heard of Gutenberg , and tried it in its early stages a few years ago, but I did not actively…

Building AI Workflows Using Alfred

Alfred has long been my launcher of choice, and I have dozens of custom workflows built for it, including lots of internal tools that help me quickly look up items in the databases of my various projects. More and more, I'm building simple AI workflows within Alfred, so I thought I'd share how I go about it. Things todo workflow Here's an example of a workflow that takes some text, and turns it…

Building a Plugin System for a React App

Examplary is a new tool we're building to help teachers create better tests and exams. One of my philosophies about product development is that the more integration and extensibility options you create within a product, the more opportunities you're creating for a community to flourish around it. As part of that, I wanted to make it possible to create custom question types. When creating an exam,…

Changing My Mind on Tech Monopolies

When John Oliver did a segment on Last Week Tonight about tech monopolies a few years ago, I was quite hesitant about the "break up big tech" message. It seemed unrealistic and difficult to achieve to me. I've fully changed my mind on this now – breaking up big tech companies, no matter how difficult (although there is sufficient relevant precedent) – is the only effective way to: Restore a…

Force Multipliers Versus Form Factors

Whenever people talk about GenAI as the 'next big thing', the lineage of previous 'big things' seems very variable on the speaker. Usually it's something like the personal computer, the web, smartphones, and now GenAI. Here's a similar version of this in a (excellent!) deck from Benedict Evans : I always find these somewhat debatable, and instinctively want to push back against it. I realised…

Using Gemini in My Todo Flow

Maybe it's taken too long, but I more and more am actually integrating little LLM tools into my daily workflows, other than coding and chat. This week, I've added Gemini into my flow to add tasks into Things . I often will receive a Slack message that contains a task, or will reply to someone saying "Okay, I'll take care of doing X later today." Rather than having to turn this into a task in…

Rendering a PDF Page to a JPEG in Node.js

PDF.js is one of those libraries that seems amazing in its scope – it's able to render basically any PDF you throw at it into an HTML canvas, with all of the weirdness of the PDF file format. It's also confusing and not super well documented. I had to implement a use case where I wanted to let users select a section of a page from a PDF to embed it as a 'clipping' in their text document. My rich…

Integrating Safe Exam Browser

Safe Exam Browser is an app that can be installed on Mac, Windows and iOS to create a safe test taking environment for students that prevents some basic ways of cheating, e.g. by switching to other apps, or copying and pasting content. It's supported by Moodle and various other e-learning tools. Implementing it is not super difficult, but is not made very easy by the fact that the developer…

You Don&#x27;t Need Everything in One Place

There's this urge that I – and based on the subreddits I visit, a lot of people that are into productivity tools – have, of wanting to find a perfect system where all of your notes and data are in one place. That makes you look for tools that do everything. I long thought that my perfect notes app could also hold all of my tasks and contacts and meeting info. That's not true. This starts the fact…

Keeping Your Environments Distinct with Separate Favicons

When you're working on a web app that runs in multiple environments (dev, staging, production), it's easy to get confused and get frustrated about a change not working, only to realise that you've been refreshing the production app rather than your locally running version for the last 5 minutes. To keep it easier to separate them, I like to set custom titles and favicons based on the stage: In…

Staying in Control Using Remote Config

Apple and Google's app review process for getting a new version of an app released into the App Store and Google Play is pretty fast these days, often taking less than 48 hours, and frequently even under 24 hours. That's still a long time to wait if you need to deal with an urgent bug or have information you want to communicate with your app users. That's why it's important to build in some…

Norms and Rules in Online Communities

I've been spending a lot of time recently thinking about the governance of online communities. One helpful starting point for this is the Preece & Maloney-Krichmar definition of online communities: The people who come together for a particular purpose , and who are guided by policies (including norms and rules) and supported by software My thinking has always been that communities need to develop…

Building Dynamic GraphQL Queries in Flexible Feed

Flexible Feed is a small Shopify app I built a few years ago. It helps merchants create XML and CSV feeds to use with Google Shopping and other similar platforms. At the core of it is this simple feed attribute mapping UI: Here, you can configure what fields are available in the feed, and where the data for this field should be pulled from. Even though it looks pretty minimal - there's a lot of…

Using AI in GitHub Pull Requests

We've been talking for a while about how we can be better at informing the Street Art Cities community about new features and changes to our platform. Our main way of sharing this info is through our forum , but sometimes it's hard to remember to share an update about a small change. To encourage ourselves to do so, I built a little bot that will automatically create a draft forum post for every…

Musing on Writing

I've been reminiscing a lot lately. I've always done that, but these moments when you make big life changes cause that to be so much more intense. It makes you appreciate what you have, what the world around you contains. I've been thinking a lot about what I used to do back in high school, during classes that I found boring. I'd write these stories, all very short, I couldn't write something that…

TraceStack: Building an Open-Source Serverless Observability Tool

Last summer, I started building TraceStack : A serverless observability tool for AWS Lambda. My goal was to build an open-source self-hosted alternative to Lumigo or New Relic , focusing on tracing Lambda invocations of Javascript functions. I have a lot of small projects I run, where some observability would be nice, but I quickly run into the limits of what free tiers of these hosted platforms…

AI Concepts for Developers

I've been doing some diagrams to explain certain AI app development concepts to my team. Here they are, if they're useful to anyone else. Created using the delightful FigJam app from Figma . Retrieval-augmented generation (RAG) Retrieval-augmented generation (RAG) is when an AI looks up information from a database or documents to help it give better, more accurate answers. Tools/actions Tools or…

Lazy Loading Routes with Vite and React Router v7

Recently, we switched the Street Art Cities dashboard (where users upload artworks, create routes, view insights, etc.) from a massive monolithic Next.js app – that also contained the many pages of our website! – to a standalone React app using React Router and Vite . We have dashboard for various different roles - hunter, artist, country manager, admin, etc. To make sure not everyone needs to…

Setting Up Syntax Highlighting in HubSpot CMS

I've been building out our NearSt Engineering Blog over the last couple of days, and one of the things I ran into is the need to show syntax-highlighted code in some of the blog posts. HubSpot CMS doesn't have a built-in way to do that, but you can easily add this yourself. In the HubSpot Design Manager, you'll want to find your blog post template (often called something like blog-post.html in the…

Building an Interactive Markdown Textarea

I really love the markdown editor on GitHub, especially how easy it is to drop in files and images. It's still all plain text, but it works interactively. If you drag and drop a file, it uploads that file and inserts a link into your text. If you paste an image, it uploads it and inserts an markdown image tag. I was trying to replicate this behaviour recently, and realised it's a lot more…

Creating Awesome Embeddable Scripts

I was looking at a video about the new version of Apple's MapKit JS , their web maps library, and they showed how to initialise the SDK by loading the script. The code looks something like this : < script src = "https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js" crossorigin async data-callback = "initMapKit" data-libraries = "services,full-map,geojson" data-token = "Your MapKit JS token" ></ script…

Switching My Blog to Zola

For a while now, my personal website has been built using Next.js , running on Vercel . It was working fine, but it felt a bit slower than it should be, and quite heavy for something that is basically a static site. I've also not been super happy with Vercel's reliability lately. Google's indexer doesn't seem to be super happy crawling the site, occasionally reporting network errors accessing…

Building Composable React Components

Imagine you're building a <Button> component. You might add some props to control the appearance, like variant or size . You end up with a component that looks like this: const Button = ({ variant , size , children }) => { const className = getCorrectStylesFor ( variant , size ); return ( < button className ={ className }> { children } </ button > ); } Okay, but now you run into a problem. This…

Pro Software for the iPad

I've complained about it before, but I still don't understand the Apple pundits that come out every year when new iPads are released to complain about the iPad software not supporting enough pro workflows - usually with the request to have some way to dual boot or virtualise macOS. On an iPad! I really don't get why you'd want that. No experience of macOS is going to be better than using it on a…

Writing JSDoc for React Components

I'm starting to get into the habit of consistently writing JSDoc comments for my front-end components, which helps with explaining usage of a component, and gets rid of VSCode’s red squiggles because it can't figure out the type of certain props. Here's some simple examples of how to use JSDoc effectively to better document your React components. Simple example /** * A great component that…

Tags - From UX to Implementation

Recently, I've become a really massive fan of tags and attributes as ways of allowing users on the platforms I build to structure data. My main note taking app Obsidian supports both of these concepts as well, and they basically eliminate the need for folders and file name structures. I've basically adopted a single level file structure, where all notes live in the same single folder. Tags and…

My Second Day on the California Zephyr

I woke up at around 6am after my first night's sleep on the train. It took me a few hours to get to sleep, but once my body was used to movement of the train, I slept soundly for a good few hours. After freshening up a bit, I went to the dining car for an early breakfast at 6:30am. I was the first person there, but was quickly joined by a couple from Oregon. We spent a good two hours talking and…

My First Day on the California Zephyr

Our California Zephyr train left Emeryville (just outside San Francisco, a 15 minute Uber from my hotel) a few minutes late. The train attendant, Mike, that will look after our train car for the entire journey was extremely friendly and personable. Within 10 minutes of leaving the station, there were already beautiful views. The train wasn't just going along the coast, it was on the coast, just a…

Upgrading Your AWS Lambdas to Node 20

The Node 16 runtime on Lambda will be deprecated in June . Upgrading to Node 18 or 20 is relatively easy for most applications, with few breaking changes from Node 16. Some notable additions are the native support for fetch() , which you might be familiar with from browser environments, and a built-in test runner . What is more difficult, is the lack of the AWS SDK v2, which is included out of the…

Showing a Manage Subscriptions Button in React Native

If you implement in-app purchases in your app, you might want to show users a way to manage that subscription by showing links to the App Store or Google Play app. In native Swift, on the iOS side, there is StoreKit to do this, and Android has similar APIs. If you're using React Native , however, you might prefer to just deep link to the right URL. Code example A very simple approach to doing this…

Launching Flexible Feed

I released my first Shopify app a few years ago. It’s called Flexible Fulfillment , and it helps store owners distribute orders to t hird-party vendors, often used for dropshipping. Today, I’ve released my second app: Flexible Feed . It’s a pretty simple app that makes it easier to create product and inventory feeds from your Shopify stock. This allows you to connect NearSt easily, or set up feeds…

A Mental Framework for Debugging

Engineers spend a lot of their time debugging issues. It's a skill you acquire over time, and something that each engineer does in a slightly different way - a personal style. Over time you get better at spotting patterns and finding the tools that help you trace down problems or mistakes. In addition, you'll develop a (explicit, or more likely implicit) mental model or process you follow to debug…

Building a Serverless Search Engine Using Fuse.js

One of my side projects that gets considerable traffic finally used up all its free Algolia credits. I love Algolia because of how easy it is to set up, with great SDKs, both for managing your search index, and its ready-made front-end UI components for React and many other front-end environments. My use case in this project is really simple though - a data set of about 50,000 items, and only need…

About Me

I'm a software engineer and startup founder from The Netherlands, currently living in London. I love creating well-designed user interfaces for real people and crafting tools for developers to build better software. Looking for something fun to do while waiting for my mom, a primary school teacher, to finish grading her students’ homework, led me to start programming at age 11. At age 16, I…

Going back to Obsidian

Back in 2020 and 2021, I was a heavy user of personal knowledge management (PKMS) tools. Initially Roam Research , where I built up a graph of around 1000 notes, and next Obsidian, when I wanted more control and ownership over my notes. Last year, at some point, I stopped using Obsidian, in an attempt to go back to a simpler setup for my note taking. Drafts: where text starts That journey lead me…

Advent of Code 2023

Advent of Code has started again! There's always a question around what language or tool you use to complete the challenges. This year, I decided to keep it simple, and keep it to what I know best: Javascript. I did want to try a new way of running Javascript though, something I've thought about before but never had a chance to try: Jupyter Notebooks. If you're not familiar with Jupyter , it's an…

The iPad as a Platform

I've used an iPad since the very first year it was introduced. Me and my co-founder Dante managed to convince our school's headmaster that our class should really get iPads to use the e-learning platform we had built, and weirdly the school agreed. I didn't have an iPad for a while after high school, until I bought one a little over a year ago in an attempt to stop bringing my MacBook home with me…