Roast my Boilerplate Application Idea

Roast my Boilerplate Application Idea

Throughout my adult life, I have built side projects for fun, to learn, and almost always with the dream of someday building something profitable enough to start a business. I've taken stabs at games, development tools, productivity tools, forums, chat apps, and more. Despite the plethora of modules and libraries available in language-specific package repositories (like NPM), I've always found the full-stack options disappointing. Most libraries aim to solve one particular problem at a specific point in the application stack. While I am generally a fan of the Single Responsibility Principle, at some point, we need to actually put the pieces together intelligibly and build an application.

In an established business, the usual way of assembling these pieces is to program an application that integrates them from the ground up; this is very time-consuming. From my reading on IH and elsewhere, smaller businesses tend to opt for services instead, using platforms like Firebase to set up standard features like authentication quickly. But, using third-party integrations inevitably becomes painful over time. When we're making requests to a third party over an API, we don't own the data. This situation sucks for many reasons:

  • API Runtime Adapters: We must issue API requests and handle responses and their corresponding failures at runtime. We must deal with possible outages and convert requests and responses into appropriate representations in our system. When we don't store our data in an API, we can use one set of types throughout the application to eliminate possible runtime errors at compile time. When we store our data in an API, we must manually test our workflows using sandbox credentials that need to be toggled off in production, which is time-consuming and far more error-prone.
  • Maintain Credentials: We need to configure and maintain credentials and updates for the integration. Even if the API is properly versioned, we must update whenever older versions are deprecated; when we're using the API to solve a simple problem, this tends to be more painful than helpful.
  • Behaviors: Since we don't control how the API works, we have no path forward if it doesn't work how we need it to except to find a completely different solution.
  • Migrating: When we decide to pursue a different solution, we are at the mercy of the original provider to export our data in a platform-independent format. We'll have to write an adapter or jump through manual hoops to migrate to a different platform if they don't. In the worst-case scenario, we can't even get our data or get it in a workable format.
  • Monitoring and Failover: We cannot centralize application monitoring or ensure adequate failovers. To see issues with a third-party service, find logic errors, or track through runtime errors, we must jump to third-party dashboards to get access to our data. Here, too, we are at their mercy regarding what information they decide to expose. We cannot simply put debug logs in their service or view their backtraces to find the source of an issue. Therefore, debugging processes become more investigative and less straightforward.

The benefit of a service is that we don't have to host it ourselves, and it's the only way to get access to the desired implementation when the underlying code is not open-source. When a service brings a lot to the table, this tradeoff slants in favor of using a service (I don't want to write or host my email server). However, in situations where the underlying behaviors are prone to frequent change and have simple hosting requirements, the tradeoff slants heavily towards hosting it yourself, which is why I crave access to code that solves problems across the entire application stack; it's the best of both worlds: Host it yourself, change it however you want, manage it all in a single deployment.

Full-stack, self-hosted code has historically come in three flavors: CMSes, frameworks, and boilerplates/templates. CMSes tend to be too rigid to develop bespoke applications (although interesting things like KeystoneJS blur the line between a CMS and a framework). I like the idea of a good framework, but I have never found one that I was really satisfied with; in particular, I have never found one that does all of the following:

  • Give you complete control over the browser without jumping through annoying hoops
  • Correctly support authentication (federated and username & password)
  • Facilitate migrations
  • Support federated authentication
  • Support automated testing
  • Support schema-based validations
  • Integrate a bundler
  • Automatically handle validations in requests
  • Provide shared types between front-end code, back-end code, and the database
  • Facilitate cross-platform code reuse

Those last two requirements are tough, and they practically narrow the field to solutions in JavaScript or TypeScript, since (as far as I know) only JS based solutions give you complete control of the browser, have a good type system, and allow you to write isomorphic code (code that works on the front-end and the back-end). Yet, JavaScript frameworks tend to lack features present in other languages' frameworks.

What About Next.js?

Next.js comes pretty close but has a few issues:

  • The most soul-crushing thing about Next.js is that it doesn't support username and password authentication, preferring to send magic email links instead. This approach is disruptive to the average user who expects a username and password signup flow to be available and people (like me) who use password managers. In the password manager case, you've forced the user to navigate to their email, wait to receive it, and then click a link; I'm guessing that they don't embed redirects in their magic links either, so if they were navigating somewhere before they hit the login page, they probably need to navigate back to that page after they click the link (I have not confirmed this). With a username and password form, they can just hit ctrl + shift + l or something similar.

    Next.js's lack of username and password authentication is made much worse because username and password authentication is challenging and time-consuming to implement correctly. On the surface, it's just, "Check the username and password and set the user information on the session." But, it's really much more than that: You must be aware of and prevent enumeration and timing attacks in any part of the application that might expose user data; mitigate brute force attacks without introducing account lockout vulnerabilities; implement global rate limiting to prevent attackers from attempting common passwords across your whole authentication surface; backup user data; provide users with a way to reset usernames and passwords; generate, send, and expire validation emails; provide UI that enforces password strength; choose an appropriate hashing algorithm and update it over time; ensure that error messages to don't reveal any sensitive information; select a secure session store and configure it properly (Redis is currently impossible to configure correctly on Herkou since they don't support TLS); ensure that your server does something appropriate to prevent XSS attacks (the default browser protections are insufficient); and more! You can work through the OWASP documentation and spend a lot of time reviewing your code to get it right. But it's not easy and certainly not what you want to spend time doing.

  • It has support for TypeScript through Zod but still requires manual work to implement validations (including responding to validation failures).

  • It's opinionated, and I would rather write or edit a little bit more code than navigate Next.js's conventions.

  • It uses Webpack as the bundler, which is painful to configure if you need to support custom builds. I prefer Parcel.js

So, I started building Sapling as a powerful, mostly monolithic application boilerplate that gives me total control. It's a clean, legible app built with a TypeScript / React front-end and a TypeScript / Node.js back-end that comes with out-of-the-box support for username and password authentication, payments and subscriptions (Stripe), migrations, validations, request and response wrappers, bundling, automated testing, and more. It brings me joy to know that it's waiting for me when I start my next project, and I'm eager to hear what you think about the idea. Would you buy it?

Stay up to date

Get notified when I publish something new, and unsubscribe at any time.