Jan MiksovskyArchive AboutFeedSearchContact

2026

Code is more concise than configuration: comparing a sample blog in Web Origami and Eleventy

This post is the fourth and last in a series comparing the same sample blog in Web Origami and Eleventy:

As a very crude metric, the conciseness of legible code can roughly correlate with simplicity, so in this final post, let’s measure how large the projects are. That analysis is followed with an Appendix of other notes from this experiment.

I totaled the size of all source files in each project: configuration code, data, scripts, and templates. I did not count markdown content as source code, and in any event, both projects use the same markdown.

In a code size comparison, Eleventy has latent advantages: its folder structure implicitly encodes behavior that Origami must spell out in code, and its use of configurable plugins should in theory require less code.

Counting source code in bytes (with wc -c) for Eleventy:

    2836 base.njk
      74 blog.11tydata.js
     157 blog.njk
      49 content.11tydata.js
    4610 eleventy.config.js
     475 eleventyDataSchema.js
    1380 filters.js
      54 home.njk
     864 index.njk
     294 metadata.js
    1199 post.njk
     528 postslist.njk
     519 sitemap.xml.njk
     608 tag-pages.njk
     235 tags.njk
   13882 total

And for Origami:

    1613 base.ori.html
     144 blogIndex.ori.html
     603 feed.ori
     310 images.ori
     403 index.ori.html
     243 metadata.yaml
     851 post.ori.html
     501 postList.ori.html
    1463 posts.ori
     502 readableDate.js
    1328 site.ori
     201 tag.ori.html
     251 tagIndex.ori.html
     159 tags.ori
    8572 total

The Origami version, which is doing much of the work from scratch, is smaller!

It seems Origami’s smaller size can be attributed in part to:

But the biggest difference by far is that the top-level Origami site definition in site.ori takes much less code than eleventy.config.js (which configures the main behavior of Eleventy’s static site generator). In the domain of site creation, code is indeed more concise than configuration.

Speaking of metrics, performance should probably be no more than a secondary concern for you when evaluating blogging tools. Most static site generators are quite fast, especially for personal sites.

That said, I timed builds of both approaches via time npm run build on a 2024 MacBook Air M3. I threw the first (longest) time away, then averaged the real time of the next three builds.

Both Origami and Eleventy build this tiny blog project in less than a second:

While performance shouldn’t be your primary concern, in the case of this sample blog Origami comes out ahead. It’s entirely possible that Eleventy has higher startup costs as it digs through your project looking for files, so for all I know, once those startup tasks are accomplished it could process a larger blog faster. Regardless, the performance here shows that Origami’s fundamental approach is well-suited for this task.

Conclusion

I interpret these results as demonstrating that an explicit, code-oriented solution like Origami is easier to follow, more coherent, more expressive, and more concise than one predicated on configuration like Eleventy. I expect the same comparison holds true for the countless other static site generators that rely on a combination of configuration, naming conventions, and folder structure.

Not every person wants to code, or has the time or energy to learn to code. But I think configuration-based site generators cover up the complexity of code with a system that is ultimately just as hard to understand. If you’re capable of configuring a tool like Eleventy, you are just as capable of coding in Web Origami.

I don’t know the Eleventy team personally, but they seem like perfectly nice people who care deeply about their users and want to create good tools for them. They also have what looks (to an outsider like me) like a dynamic, supportive user community. If you pick Eleventy for your site, it’ll probably work out fine.

If you’re interested in trying Origami for a blog, I think you’ll like it. You can start with the corresponding origami-blog-start template project. If you’re interested but want help or have questions, let me know.

Other posts in this series:

  1. Code is easier to follow than configuration
  2. Code is more coherent than configuration
  3. Code is more expressive than configuration
  4. Code is more concise than configuration [this post]

Appendix

The following are small points I noticed while studying the Eleventy blog; none are as important as the main points above.

Bugs

I found three very minor possible issues in the sample eleventy-base-blog project. Although the issues are small and debatable, any bugs in a template project will be endlessly copied into new blogs, so their potential impact is magnified.

I want Eleventy to continue growing and their new users to have great experiences, so I reported these to Eleventy (issue, issue, issue).

JavaScript as a template language

Template languages like Nunjucks are common, but they become another language you need to learn. If you already know JavaScript, that’s enough to be able to do anything you want in a template in Origami.

As a bonus, this means that you don’t have to do special things to invoke JavaScript from a template. When the Eleventy version wants to insert a timestamp onto a page, it registers a small JavaScript function as a shortcode:

eleventyConfig.addShortcode("currentBuildDate", () => {
  return new Date().toISOString();
});

This shortcode can then be called by name from a Nunjucks template:

built on {% currentBuildDate %}

In Origami, a template is a JavaScript template literal stored in its own file, so you can skip all that registration complexity above and just inline the desired JavaScript into the template:

built on ${ new Date().toISOString() }

If the code were longer, you could put it in its own JavaScript file and call that by file name. The Origami version uses that technique to implement the Eleventy readableDate function; Origami templates then call that function with

${ readableDate.js(post.date) }

Passing data to templates

A number of the Nunjucks templates in the Eleventy blog include lines like this:

{% set postslist = collections.posts %}
{% include "postslist.njk" %}

As I understand it, a Nunjucks include doesn’t let you pass data directly, so you have to pass data via what’s effectively a global variable. That approach is so fraught with the high potential for errors that it’s hard for me to recommend any system that requires it. (Note: Eleventy allows the use of template engines other than Nunjucks; perhaps those are better.)

Origami templates are functions, so you can pass data to them directly:

${ postList.ori.html(posts) }

Focusing on representing pages

Blogging tools like Eleventy use a project’s folder structure to determine the resulting site structure. That approach focuses on complete file resources, such as a page for an individual post.

But blog posts in this project actually have three representations:

  1. The post page in the blog area
  2. A post entry in lists of posts: home page, blog/index.html, and tag pages
  3. A post entry in the feed at feed/feed.xml

The folder structure only gives you a way to conveniently express the first representation. The Origami code doesn’t have any particular focus on pages; all post representations can be defined in a variety of ways.

Meanwhile, using folder structure to represent site structure has limits. It took me a while to realize that the single file tag-pages.njk isn’t just a template for a tag page; an embedded block of JavaScript at the top of the file appears to also generate the collection of pages like tags/second-tag/index.html.

In contrast, the Origami site.ori file includes an explicit definition of the tags area.

Inlining CSS

The Eleventy blog inlines the main CSS stylesheet into every page instead of linking to it. Origami can easily do both, but as a matter of preference, I had the Origami version link to the main stylesheet. Among other things, that keeps each built page smaller and easier to read.

The Eleventy version uses an Eleventy navigation plugin. I’m probably missing something, but in this project it looks like the plugin is used to add an aria-current attribute to three links.

Perhaps in other contexts the plugin saves time, but here it seems like overkill. I implemented this in the Origami version by adding conditions to the three links in question:

${ _.area === "Home" ? `aria-current="page"` : "" }

This does the job, and is a lot easier for me (someone familiar with JavaScript) to understand. This could be scaled up into a helper function if necessary.

HTML rewrites

I was baffled by this template fragment in the Eleventy version:

Go <a href="index.njk">home</a>.

I just couldn’t figure out what this was doing — what would it even mean to navigate to a Nunjucks template? Looking at the running site, I could see that the link was magically being rewritten. But even closely reading the project’s source code couldn’t help me see how or why this was happening.

Claude Code identified this fragment as something handled by the Eleventy InputPath to URL plugin.

Some people love such magic; I’m not one of them.

Sitemaps

The original Eleventy project defined a sitemap.xml file so I implemented one for the Origami version. That said, a sitemap seems unnecessary for this blog; all the content is trivially discoverable by a search engine. The code to generate the sitemap ends up being both a distraction and a possible maintenance burden.

Plugins are general features bound to specific projects

The original Eleventy blog uses a number of plugins to:

All these tasks performed by the Eleventy plugins have one thing in common: the tasks have nothing to do with Eleventy. Every one of them is something you might want to do on any static site, regardless of which tool you’re using to make it.

It’s in the nature of tools with proprietary interior workings to require tool-specific plugins. The lost opportunity is that, instead of sharing general-purpose code that can work with many tools, we collectively waste time rewriting the same ideas over and over for different tools.

Extensibility in Origami is provided by calling functions that can be written as generally as possible. There are no internal data structures that require proprietary plugins to manipulate. As the creator, your own code generates the requisite objects; you can pass those directly to third-party functions.

The aforementioned Origami package for turning a data object into an RSS feed is a plain JavaScript function that has nothing to do with Origami at all. Other Origami packages are written around the use of the standard Map class as the basis for tree structures, an approach that’s at least theoretically adoptable by other tools.

Code is more expressive than configuration: comparing a sample blog in Web Origami and Eleventy

This post is the third in a series comparing the same sample blog in Web Origami and Eleventy:

This post looks at another advantage of code over configuration: the degree to which you can easily express your ideas without limits.

As one example, let’s look at the code required to give this blog a feed. The Eleventy version uses the Eleventy RSS plugin, which in this project is configured this way:

eleventyConfig.addPlugin(feedPlugin, {
  type: "atom", // or "rss", "json"
  outputPath: "/feed/feed.xml",
  templateData: {
    eleventyNavigation: {
      key: "Feed",
      order: 4,
    },
  },
  collection: {
    name: "posts",
    limit: 10,
  },
  metadata: {
    language: "en",
    title: "Blog Title",
    subtitle: "This is a longer description about your blog.",
    base: "https://example.com/",
    author: {
      name: "Your Name",
    },
  },
});

In contrast, the Origami project uses a function that generates an RSS feed from a data object created this way:

// The posts in JSON Feed format
(posts) => {
  version: "https://jsonfeed.org/version/1.1"
  title: metadata.yaml/title
  description: metadata.yaml/description
  feed_url: `${ metadata.yaml/url }/feed.json`
  home_page_url: metadata.yaml/url
  
  // Map the post data to JSON Feed items
  items: Tree.values(Tree.map(posts, (post, slug) => {
    // Patch image URLs to be absolute
    content_html: post._body.replaceAll('src=".\/', `src="${ metadata.yaml/url }/blog/${ slug }/`)
    date_published: post.date
    id: url
    title: post.title
    url: `${ metadata.yaml/url }/blog/${ slug }`
  }))
}

In both projects, you build the feed with code — but in completely different ways:

The expressiveness of code gives you the freedom to tackle things the way you want to, where the code required to make the change is proportional to the complexity of the change. If you want to change the Origami feed, you can change the code above.

A long-term benefit of coding things is that you learn transferrable knowledge. Your potential mastery of the Eleventy RSS plugin data schema won’t help you in a different blog tool, or even using a different Eleventy plugin. In contrast, learning an interchange format like RSS or (here) JSON Feed is knowledge you can apply elsewhere, as are the data manipulation techniques employed in the code above.

[As I was finishing this post series, I discovered that the Eleventy RSS plugin allows you to specify a feed template, giving you the same degree of expressiveness as Origami although not as concisely. But that only makes me wonder why the plugin has to exist at all — the feed template itself isn’t much longer than the plugin configuration code.]

The expressiveness of code comes into play at every level of the Origami site. At the site’s highest level, I could readily use Origami to support the Eleventy sample blog’s preferred folder layout:

I’ve never used this particular layout for a project before, but in code it was straightforward to implement.

I point this out because folder-based, configuration-focused tools impose very particular demands on how you organize your content and source files. I have no idea whether it would be possible to configure, say, Astro to work with the content layout of this Eleventy project, or vice versa.

You might not care about that, or you may care about that a lot. In an Origami project, there’s nothing special about the source file names or organization; you can structure them however makes sense to you.

To be clear, both approaches require too much code! I hope someday you can make a great blog for yourself with little or no coding, one that lets you design whatever you want, is firmly under your complete control, and doesn’t ransom your own creation to you for a monthly subscription.

But we have to start somewhere. Given that today both projects here start with a bunch of code, you might as well think about what code you will need to learn, and whether it will let you create what you want without limits.

Other posts in this series:

  1. Code is easier to follow than configuration
  2. Code is more coherent than configuration
  3. Code is more expressive than configuration [this post]
  4. Code is more concise than configuration [coming]

Code is more coherent than configuration: comparing a sample blog in Web Origami and Eleventy

This post is the second in a series comparing the same sample blog in Web Origami and Eleventy:

Today let’s look at how both projects define the overall structure of the site and consider whether they can present a coherent picture of what you’re building.

Like most static site generators, Eleventy leverages the natural tree-like structure of a folder hierarchy. The good news is that the file system itself is the best picture you’re going to get of the resulting site. That’s also the bad news.

Here’s the folder structure of this Eleventy project, including all relevant source files:

_config/
  filters.js
_data/
  eleventyDataSchema.js
  metadata.js
_includes/
  layouts/
    base.njk
    home.njk
    post.njk
  postslist.njk
content/
  blog/
    blog.11tydata.js
  blog.njk
  content.11tydata.js
  index.njk
  sitemap.xml.njk
  tag-pages.njk
  tags.njk
eleventy.config.js

The above organization alone may not mean much to the uninitiated, and sadly folders on their own can’t have comments. Experienced Eleventy developers can presumably envision the resulting site, especially if they also scan the lengthy configuration file.

There are also many little files that configure different parts of the site’s construction, like content/content.11tydata.js:

export default {
  layout: "layouts/home.njk",
};

Most of these configuration files had no explanatory comments, by which I only conclude that we’re not expected to look at them. But if the average user isn’t expected to look at these files, why not have comments for those users that do look at them?

The above file is setting a path to a Nunjucks layout, but I couldn’t see how it was used. I later learned that putting a file called content.11tydata.js inside a folder called content implicitly associates that data with the folder. In this case, it defines a default layout property that will be applied as the base template for other templates in the content folder, like content/index.njk.

Most of the Eleventy configuration code feels like this. The site builds a blog as advertised, but it feels like substantial work to piece together the site’s construction to the point where you could change it.

In contrast, the premise of a coding-focused approach like Origami is that you describe what you want in code. Given that freedom, most Origami users elect to define their site’s top-level tree of resources in a single file, providing a coherent map of the project. Here’s the whole site.ori file for the sample blog:

// This file defines the structure of the entire blog site
{
  about: {
    // About page
    index.html: templates/base.ori.html(Origami.mdHtml(about.md))
  }

  // Static assets like stylesheets
  assets/

  // Blog area
  blog: {
    // Blog index page
    index.html = templates/blogIndex.ori.html(posts.ori)

    // Create a folder for each post
    ...Tree.map(posts.ori, {
      key: (post, key) => `${ key }/`
      value: (post, key, tree) => {
        // Index page for post folder
        index.html: templates/post.ori.html(post, key, tree)
        // Any associated images
        ...post.images
      }
    })
  }

  feed: {
    // Blog feed in RSS format
    feed.xml = Origami.rss(feed.ori(posts.ori))
  }

  // Home page
  index.html = templates/index.ori.html(posts.ori)

  // Tags area
  tags: {
    // Tag index page
    index.html: templates/tagIndex.ori.html(tags.ori)

    // Create a folder for each tag
    ...Tree.map(tags.ori, {
      key: (group, tag) => `${ Origami.slug(tag) }/`,
      value: (group, tag) => {
        index.html: templates/tag.ori.html(group, tag)
      }
    })
  }

  // Not Found page
  404.html = templates/base.ori.html(Origami.mdHtml(404.md))
}

// Add a sitemap for all of that
→ (site) => {
  ...site
  sitemap.xml = Origami.sitemap(site, { base: metadata.yaml/url })
}

Even if you don’t know Origami or JavaScript, you can probably squint and perceive the structure of the final site. For example, at the top you can see that the about area contains a page called index.html.

I think such a coherent, text-based map of the site is enormously helpful in understanding and remembering how the parts fit together. You can also ask Origami to draw a visual diagram of the running site to confirm your understanding.

Other posts in this series:

  1. Code is easier to follow than configuration
  2. Code is more coherent than configuration [this post]
  3. Code is more expressive than configuration [coming]
  4. Code is more concise than configuration [coming]

Code is easier to follow than configuration: comparing a sample blog in Web Origami and Eleventy

This post series is for people who want to build or rebuild a site.

You may have heard of Eleventy, a popular static site generator, and maybe heard it’s simple to use. To evaluate that simplicity, I’ll compare a sample blog in Eleventy to the same blog in Web Origami. This will be similar to my comparison last year of Astro and Origami.

If you’re shopping for a site-building tool, I hope this series can help inform your decision. If you already use Eleventy, I’m happy you’ve found something that works for you. As I said last year, anything that makes people more likely to create a site is fantastic.

A difference in strategy

Eleventy works like most static site generators: you run the tool, it searches inside your project for certain folders and files, then processes them to create an output directory with your site’s HTML pages and other resources. You influence this process through configuration, setting various parameters to adjust what Eleventy does. You generally set those parameters through JavaScript files, although the emphasis in those files is on defining parameterized objects or enabling plugins.

In Web Origami you focus on defining the site you want with code. You do this in standard JavaScript or the smaller Origami dialect of JavaScript, which is essentially JavaScript expressions with embedded file paths. The code does whatever you tell it to do. In this case, it defines a blog site’s tree of resources, transforming the markdown posts into browsable HTML and a feed.

This difference between configuration and coding is similar to the difference between working with numbers in Intuit QuickBooks and Microsoft Excel. The former is configured; the latter lets you calculate whatever you want.

Configuration is generally sold as simpler than coding, and most people intuitively feel that should be the case. But I believe that, for making sites, coding is superior in four specific ways:

  1. Code is easier to follow than configuration.
  2. Code is more coherent than configuration.
  3. Code is more expressive than configuration.
  4. Code is more concise than configuration.

Configuration can certainly let you achieve impressive results in complicated domains that you probably couldn’t code yourself, but sites just aren’t that complicated. It’s actually easier to code your own site from scratch than to create one by configuring a tool.

Experiment setup

I copied Eleventy’s recommended starting point for new blogs, the eleventy-base-blog template project, studied that until I felt I understood its construction, then ported it to Web Origami. This gave me two versions of the same blog:

Both demos are about as close as I can easily make them. For a cleaner comparison, I made a few modifications to the original Eleventy project:

  1. The original project had an introductory message with instructions to remove it, so I removed it.
  2. The original used a plugin for image optimization, but reproducing the effects of that would complicate this analysis, so I removed it.
  3. I removed the original’s XSLT stylesheet for the blog feed, as XSLT is being deprecated by Chrome; WebKit and Gecko will likely follow. (I’m not saying the deprecation is warranted, but given the state of things I felt the stylesheet was a distraction.)

Beyond that I tried to port all observable behavior; some minor differences remain. For example, the Eleventy project uses PrismJS for syntax highlighting while the Origami solution uses the slightly different HighlightJS. With more work, the sites could be made even more identical, but I don’t think that would change the overall results of this experiment.

Code is easier to follow than configuration

With two versions of the same project in hand, let’s start evaluating them by considering which version is easier to follow. If you’re coming fresh to the project, can you answer the question: How does it work?

That can be a hard question, so let’s start with a simpler one: What is calling what?

For template projects like eleventy-base-blog, the README typically instructs you to build the site with a command like npm run build. That’s the main entry point to the build process. I tried to search forward from there and follow links to related files.

I got stuck.

I constructed a partial map of what calls what:

The files floating in space aren’t directly referenced by any other files. Some of the file names suggest what roles those files play, but it was still mysterious to me how they actually played those roles.

I eventually found an Eleventy documentation page called Order of Operations providing an “advanced” description of most (but not all) of what was going on. I then had Claude Code guess/explain how the remaining files worked. This clarified that, e.g., Eleventy lets you register JavaScript functions as “filters” you can call from templates. I hadn’t been able to work out for myself that many of the .njk files were invoking code in filters.js.

I was then able to flesh out the above dependency diagram, adding what I understand to be the implicit connections as dashed lines:

Many of the connections in this project are dashed lines representing “action at a distance” — if you don’t already know how the system works, such connections are hard to discover or intuit. This may be acceptable for something you will use all the time, but it certainly does make learning the system (or coming back to it) more difficult.

Let’s now try to answer the “What is calling what?” question for the Web Origami blog, again starting from the build command:

ori copy src/site.ori, clear files:build

Even if the meaning of that command is unclear, you can still see an explicit reference to the file site.ori. If you open that file, you’ll see it contains references to all the files it calls.

You can repeat that process, following links from one file to another, to recover the entire graph of source file calls:

The Origami project has no hidden associations, so all the lines are solid. Everything happens because an explicit line of code makes it happen.

This property of an Origami project makes it much easier to follow what the project does. When I read someone else’s Origami project, it doesn’t matter how they’ve written it. I can always start at the build command and work forward to find all the code. The project’s author also benefits from this same guarantee when they read their own project after some time away from it.

Other posts in this series:

  1. Code is easier to follow than configuration [this post]
  2. Code is more coherent than configuration [coming]
  3. Code is more expressive than configuration [coming]
  4. Code is more concise than configuration [coming]

Who else would use a shared Electron library to create and deploy Netlify sites?

I’m interested in helping to create a shared JavaScript library for letting Electron apps authenticate with Netlify (and potentially GitHub/GitLab pages) via OAuth for the purpose of creating new projects and uploading files to existing projects.

The users in the web publishing ecosystem are benefiting from Electron becoming a de facto standard for user-facing tools. As Niki relates, Electron is winning because “native has nothing to offer”. Many developers are voting for Electron these days; I’m one of them.

One task I want my Electron app to perform for end users is helping them select or create a site on a static site host and later deploy locally-built files to that site. Netlify is an attractive target because it supports OAuth; GitHub Pages and GitLab Pages are others.

As it stands today, many tools that want to perform this task on the user’s behalf msut guide the user through creating an account with a host, obtaining a developer credential such as an access token, then copying the token and various other details into the tool. This is complex enough for a developer — and ridiculously complex for a non-developer. I think Publii’s walkthrough of this process is as clear as possible and it’s probably still daunting to many people that might want to create a site.

Netlify offers developers the possibility of an OAuth-based UI flow, but that’s a non-trivial thing to create from scratch:

Even with the help of AI, creating and maintaining this would be some work. But much of this work would be generic — so it could be implemented in a library shared by multiple tools.

A conceptual, back-of-the-envelope API sketch for a hypothetical NetlifyPublish library:

// Select the site or create a new one
const siteDetails = await NetlifyPublish.selectSite({ name: "My blog" });

if (siteDetails) {
  // Successful, deploy build to site
  const files = await doTheBuild(); // However the tool wants to do that
  const success = await NetlifyPublish.deploySite(siteDetails, files);
}

There are many details to hammer out — how is work split across the main Electron process and the renderer? To what extent can the server component be generalized and shared as a community service? On the renderer side, how is the UI made modal: a <dialog> or a separate BrowserWindow? How are the built files represented? How is the UI themed? etc.

If you work on a tool that would might benefit from such a shared library, or are interested in participating in its design and/or implementation, please get in touch!

Promoting a design and development tool through comics

To increase awareness of cool features in Web Origami, I kicked off a weekly comic series with a Mastodon post:

4 comic panels

Each 4-panel comic will deliver a short, standalone story. I thought a 4-panel comic would be a perfect format for Twitter-like sites that allow 4 images per post. The images should add visual interest to a user’s feed, and the user can read the comic right there. Each comic will also be available as regular HTML on the Origami site.

It takes a couple of hours to rough out a little story, come up with code examples, ensure they work, and revise as necessary. I write the comic script in YAML, indicating who is talking, what they’re saying, and what should appear in the panel.

The rest of the process is automated:

  1. It’s easy to compile that YAML script to HTML using Origami itself.
  2. Origami can easily runs the code samples and inlines the results directly into the comic so that the terminal session and browser panels are 100% accurate.
  3. To confirm code continues to work as Origami evolves, I use Origami’s Dev.changes builtin to test the site and flag any changes in code output.
  4. I capture HTML comic panels as PNG images using Origami’s screenshot extension.
  5. I use Origami and JavaScript to upload the images to Mastodon and make a post using additional information from the YAML script.