The Level Journal

Stories about building my startup.

Read this first

Full-text search with Postgres

This week I built very basic search capabilities into Level.

In the past, I’ve used Elasticsearch. I had some hesitations:

⇨ Synchronizing data stores is a pain
⇨ It’s another piece of infrastructure to administrate
⇨ It makes it harder and more expensive for someone to download and run their own copy of Level (if they care about production-level reliability)

I decided to try out Postgres’ built-in text search features and found it to be a pleasant experience!

Here are a few resources that helped me:

⇨ The Official Manual - a well-written tutorial, worth a read through
⇨ Postgres full-text search is Good Enough!
⇨ Mastering PostgreSQL Tools: Full-Text Search and Phrase Search

Continue reading →

Building file uploads

Last week, I predominantly spent my time implementing file uploads. This was a tricker feature implement on the frontend because Elm does not have good support right now for dealing with file objects. It turned out pretty slick!


Here were my essential requirements:

⇒ Users should be able to drag-and-drop files and paste files from the clipboard onto any post or reply composer
⇒ Users should see a list of attached files on posts
⇒ Images should be embedded, and files should be hyperlinked at the current cursor position in the editor

 The backend

One of my favorite characteristics of the Elixir ecosystem (and perhaps the functional paradigm in general) is the lack of “magic” that plagues the Ruby world. In general, I’ve become pretty skeptical about adding unnecessary dependencies to Level and maintain a pretty high threshold before bringing in any outside packages.

I found Arc when...

Continue reading →

Moving to source-available licensing

I’ve made the decision to update Level‘s licensing to a source-availability scheme. This is something I’ve been giving careful thought over past few months as I evaluate my goals and my vision for the product. Ultimately, I believe it is the most sustainable way to license the codebase and will allow me to continue fearlessly building it out in the open.

 A quick tour of licensing

There are several common licensing schemes (and accompanying business models) for software businesses:

  • Closed-source and proprietary
  • The “open core” model: an open-source core version with a dual-licensed proprietary offering (e.g., GitLab)
  • Open source with a managed services component (e.g., Discourse and Ghost)
  • Source-available (similar to “open source” but with some added restrictions, such as the Commons Clause)

A majority choose to adopt the closed-source, proprietary model. The calculus is pretty...

Continue reading →

I finally learned some OTP

It took just over a year of working with Elixir before I found the need to learn more about Tasks, GenServers, Supervisors, etc. Last night, I spoke at the local Elixir meetup about my first major feature leveraging this technology.

Here are the slides!

View →

Building push notifications

I spent a few days while traveling to Seattle for ElixirConf working on push notifications. I was unfamiliar with the technologies required to send OS notifications from a browser-based application, so there was a bit of a learning curve.

Getting push notifications to work was pretty gnarly, to say the least. Here’s what Google’s guide on the Web Push Protocol has to say about it:

One of the pain points when working with web push is that triggering a push message is extremely “fiddly” […] The main issue with triggering push is that if you hit a problem, it’s difficult to diagnose the issue. This is improving with time and wider browser support, but it’s far from easy.


I plan to do a full write-up on how to accomplish this (using Elixir), but here’s the gist of how it works for...

Continue reading →

Why Phoenix.LiveView is a big deal

Last week at ElixirConf, Chris McCord announced a new project called Phoenix.LiveView. I believe this library has the potential to reshape the way many developers build reactive user interfaces on the web.

Today, Phoenix provides a mechanism for defining Elixir modules that represent different views in your application. View modules contain a special render function that returns the rendered output for the particular route (typically HTML) and any other helper functions you need for rendering.

LiveView is an extension of standard Phoenix views that will automatically propagate updates to the browser anytime state changes that would impact that view (using the existing Phoenix channels infrastructure).

Instead of sending a state change event down the wire that you have to process in JavaScript manually, LiveView sends down an updated HTML fragment, diffs it against the current DOM...

Continue reading →

My first day at ElixirConf

I flew into Seattle yesterday, one day before ElixirConf kicked off. I spent the day hitting up my favorite spots for coffee and strolling around the city. It was a productive day writing code and scribbling down ideas in my notebook.

Today began with a keynote from Jose Valim, the creator of Elixir. I came away from the talk encouraged about the future of the language (not that I was concerned before). He spoke about what to expect in future releases and stressed the criteria that the core team puts on any newly proposed additions to the core language.

Although Elixir is a relatively new language, it is quite stable. Elm’s development trajectory is similar.

As I’ve matured as a developer, my appreciation for stability has grown. One of the reasons I’m so skeptical about investing in any particular JavaScript ecosystem is the sheer volume of churn. This is becoming a tired complaint...

Continue reading →

Tracking inbox state

Today I worked on the logic for tracking the state of posts in a user’s inbox. I’ve abstained from jumping too quickly into the implementation phase, to avoid painting myself into a corner and potentially having to rewind data architecture decisions prematurely.

It’s worth mentioning a few articles that are helping guide my thinking:

→ Lee Byron’s post architecting a Facebook-like activity feed
→ The definition of event sourcing and various related articles floating around the internet

Some questions I’ve been pondering:

→ How should prioritization work?
→ Should posts ever automatically be dismissed when read?
→ How should I represent the “reason” a post is in the inbox?
→ Should I use fanout on read or write to assemble the inbox?
→ How will Level prevent inbox bloat for the user?

I’ll admit, I’ve felt a bit paralyzed by the scope of all these decisions the last few weeks. Rather...

Continue reading →

How to display server-rendered HTML in Elm 0.19

I’m building a team communication product called Level, and one of the core features is rendering posts as Markdown.

Elm has a library to handle this on the client-side. It’s a wrapper around the marked.js library that uses Kernel code call out to JavaScript land. (It appears the plan is to implement a native Markdown rendered in Elm eventually).

This library works great for doing basic Markdown rendering without additional decoration but is a bit limited if you need to do things like traverse the rendered HTML to inject special styles, or perform additional sanitization of user input.

To my knowledge, there’s not an API to traverse and modify DOM trees in Elm. It might be possible to accomplish these things on the client-side with enough JavaScript hackery, but that admittedly makes me a bit squeamish.

Before upgrading to 0.19, there was a hack for taking some server-rendered HTML...

Continue reading →

JavaScript raises my blood pressure

This morning I worked on fleshing out pagination on various pages that need it (viewing posts in a group, your “pings,” and the unified activity stream). I had previously punted on implementing it, but it’s definitely a requirement for the MVP. This was also an excellent opportunity to refactor some of the routing logic in Elm.

I ended up extracting a Pagination module that takes a Connection of nodes and renders the prev/next buttons. This is one area where Elm really shines. After all, everything – including HTML definitions – is just function.

In the afternoon, I turned my attention toward something that feels like an area of risk: Markdown rendering. When I upgraded to Elm 0.19, I switched to using elm-markdown to render post and reply bodies. The old hack I was using to inject server-rendered HTML into a <div> node was eliminated (for a good reason), so I took a crack at using the...

Continue reading →