Starting a Technical Blog in 2023

Avoid the over-engineered developer blog without giving up the tools you love.


I'm used to writing to help me think. I've been doing it throughout my education and my career. It's what drove me to start this blog. The thing that kept me from starting a blog was the amount of choices I felt I needed to make before starting.

It seemed like I needed to know the answer to a lot questions before I even started.

Should I even build my own blog? What about something like medium or substack? What framework should I use? Should I learn a new tool chain to experiment with something new? How should i organize it?

Given that I'm not the most gifted UI designer, I even had questions like?

What kind of design should I use? How do i get static assets like a logo? how do i keep my styles consistent? The last thing I want is to fiddle with css in my free time?

It took some research and a bit of perspective but I was able to address all my initial concerns. I've learned that software engineering is often a synonym for technical incrementalism. We make the fewest required changes to deliver the most value. In this case that means getting started with tools that meet 2 criteria...

  1. they are "good enough" based on some set of values. In this case a blog needs to support static content and should load fast, should be readable on any device.
  2. I can get started with them without much effort.

No solution is perfect, and what your familiar with matters more than absolute performance in a lot of cases (or everyone would be writing in assembly).

I Like React and I like working in the javascript/typescript ecosystem, so I landed on a fancy new Next.js website hosted on Vercel, with Contentlayer as the content manager.

This combination meets my needs, and the pieces fit seamlessly together. I use Next.js at work so I don't have to spend a lot of time getting up to speed on the framework. Vercel makes hosting trivial, "git push" and deploy.

Next.js

Since next now uses React Server Components, the upshot being it serves zero javascrpt by default to the browser. Combine server components and a static build process that caches each content page and you have a fast website. You can incrementally introduced client-side interactivity and server-side actions as needed. For example, I added a view counter component in minutes using Vercel KV.

I did the basic setup, then I added get operation on my home page to get the view counts for each articles

import { kv } from "@vercel/kv";
async function getViewCounts(postIds: string[]) {
  return kv.mget<number[]>(
    ...postIds.map((postId) => `views::posts::${postId}`)
  );
}

In a server component world, you call the function inside your server component and that function gets called once on the server when the page loads. Not once on the server and again on the client. You also get to run that function on the edge since that function can run in the same region as my key-value store. The edge hype is real. asynchronous components stream UI to the client. Next.js wont cache this page anymore so the server runs on every request to the back end to get a fresh count.

I increment the count in a client-side fetch so Next.js can cache each article ahead of time. Notice the use client directive, this component is your classic client component even when nested inside a server component.

First, set an api route in app/views/route.ts to increment the view count for a post in Vercel KV.

import { kv } from "@vercel/kv";
import { NextResponse } from "next/server";
 
async function incrementViewCounter(postId: string) {
  return await kv.incr(`views::posts::${postId}`);
}
 
export async function POST(request: Request) {
  const { postId } = (await request.json()) as { postId: string };
  await incrementViewCounter(postId);
  return NextResponse.json({
    success: true,
  });
}

Call the end point from a typical useEffect.

"use client";
// Next.js wont compile if you try to use a react hook on the server
import { useEffect } from "react";
 
export function ViewCounter({ postId }: { postId: string }) {
  useEffect(() => {
    console.log("incrementing views");
    const incrementViews = async () => {
      await fetch("/views", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ postId }),
      });
    };
    incrementViews();
  }, [postId]);
 
  return null;
}

Contentlayer

I've been looking for a nice interface for dealing with markdown content. The build process generates some typescript code along with your pages so you can access your content in a fully type-safe environment

Start by defining a document configuration.

export const Article = defineDocumentType(() => ({
  name: "Article",
  filePathPattern: `articles/**/*.md`,
  contentType: "mdx",
  // frontmatter metadata definition
  fields: {
    title: {
      type: "string",
      required: true,
    },
    description: {
      type: "string",
    },
    date: {
      type: "date",
      required: true,
    },
    draft: {
      type: "boolean",
      required: true,
    },
  },
  computedFields,
}));

Then we can import and render the content wherever we need it.

import { allArticles } from "contentlayer/generated";
//          ?^ Article[]

Refer to the Contentlayer docs for details about configuring the next-contentlayer plugin

Contentlayer provides a pleasant authoring experience in a lightweight solution. I write some markdown and see it live on your website. I find this combination satisfying, It meets my needs and lets me work more with react, typescript and cool new back-end technologies. I chose a basic style that feels minimal yet tasteful. I don't want to spend much more time thinking about layout than I have to. I tend to think that people respond better to more familiar layouts than to more creative ones.

This configuration also helps me focus on the content. My goal is a setup that encourages me to practice writing about challenging technical topics. I take the same approach with my coding setup. I want an environment I enjoy inhabiting while I author code. The tools should reduce the toil associated with managing websites, not drain time away from the writing. For the first time I feel like I have the right canvas to record and share my thoughts.

If you have been putting off starting a blog, I hope you found my experience helpful. Often all it takes are some tangible recommendations and a bit of encouragement and we are off to the races.