Published on

ActiveMDX Introduction

Authors

ActiveMDX is a project that lets you turn a bunch of Mdx files into structured data, so that in addition to displaying your writing in a nice way, you can automate things in the rest of the world with it as well. Whenever your writing changes, you can do things with it in the world. Whenever things in the world change, you can update your writing!

The word Active in ActiveMDX is inspired by the Active Record Pattern, and specifically, the ActiveRecord library from Ruby on Rails (a historic work of art in programming)

MDX refers to Mdx.js.

Background

Markdown is a great format for writing. It is just plain text with some markup for creating titles, lists, links that get displayed as HTML on a webpage. You can write a document in markdown, and display it as HTML without needing to clutter your text with HTML tags and attributes.

Because Markdown is plain text, it is readable as text even without being presented as HTML. Being plain text also lends itself very well to version control, and Github is great at showing you the differences between versions, among many other things. But more than anything, it is a great way to get ideas out of your head quickly and into the world.

Sometime during the mid-2010's I created a project in Ruby, called Brief, which allowed me to take advantage of these aspects of Markdown.

Brief allowed me to have different projects, which I called Briefcases, which were just a bunch of markdown files. Because they were hosted on GitHub, I could run code every time I pushed to the repository.

I used this technology to power a consulting company I started, which I will write about in the future. Essentially the idea was, as a software consultant, I had standard templates for various documents I would write from project to project. Brief allowed me to turn this writing into data that I could do things with like export to a spreadsheet, create a bunch of github issues, and things like that.

All I needed to do was get the ideas out of my head quickly, in a loosely structured way, push them up to GitHub, and a ton of sometimes tedious communication tasks were able to be automated.

Brief was amazing, but it came to be just as I (regrettably) moved away from writing software in Ruby and went full JavaScript.

Fast forward to 2022, and the Markdown landscape is insanely better than it was then. Software like Mdx.js in particular, is very exciting. It allows you to render your markdown documents with React components, which gives you a ton of power and flexibility.

As a heavy user of this, I wanted to revive the ideas behind the Brief project but instead in JavaScript.

So I created ActiveMDX.

Like Brief, ActiveMDX can help automate huge chunks of communication work that go into building software with a team.

It can even be used to generate code! Imagine what something like Github Co-Pilot or OpenAI Codex can do with very a structured set of instructions? I would not be surprised if people are able to go from english to deployed product in the next couple of years.

Turning Structured Writing Into Data

With ActiveMDX, the same basic conditions are in place:

  • I have a number of ongoing projects at any time
  • Each of these projects has a repository of markdown writing
  • The markdown writing is structured in a logical way, based on the type of document. For example, for a software project I'm working on with a team of developers, I have a folder of epics, stories, decisions, daily-standups, etc.
  • The markdown content itself is referencing things in other systems, e.g Github, Jira, Github Issues, Google Sheets, etc

So I can define a model for a particular type of document

import { Model } from '@active-mdx/core'

export default class Story extends Model {
  static statuses = ['created', 'in-progress', 'qa', 'approved', 'complete']

  get defaults() {
    return {
      meta: {
        status: 'created',
        estimates: {
          low: 0,
          high: 0,
        },
      },
    }
  }

  toJSON({ related = [], attributes = [], ...options } = {}) {
    return super.toJSON({
      related,
      attributes: [
        'description',
        'isComplete',
        'slug',
        'acceptanceCriteria',
        'mockupLinks',
        ...attributes,
      ],
      ...options,
    })
  }

  get isComplete() {
    return this.meta.status === 'complete'
  }

  get description() {
    const { document } = this
    const { leadingElementsAfterTitle = [] } = document.nodes

    return leadingElementsAfterTitle.map(document.utils.toString).join('')
  }

  epic() {
    return this.belongsTo(Epic, {
      id: (document) => document.meta.epic,
    })
  }

  get mockupLinks() {
    const { toString } = this.document.utils
    return Object.fromEntries(
      this.document
        .querySection('Mockups')
        .selectAll('link')
        .map((link) => [toString(link), link.url])
    )
  }

  get acceptanceCriteria() {
    const { toString } = this.document.utils
    return this.document.querySection('Acceptance Criteria').selectAll('listItem').map(toString)
  }
}

This model allows me to take the same document I write, and turn it into a JavaScript object.

const storyData = {
  id: 'stories/authentication/a-user-should-be-able-to-login',
  meta: {
    epic: 'authentication',
    estimates: { low: 8, high: 16 },
    status: 'in-progress',
    github: { issue: 30 },
    'estimates.high': '16',
    'estimates.low': '8',
    'github.issue': '30',
  },
  title: 'A User should be able to login.',
  description: 'As a User I would like to login so that I can use the application.',
  isComplete: false,
  slug: 'a-user-should-be-able-to-login',
  acceptanceCriteria: [
    'A user can visit the signup form, supply their name, email, and password',
    "The signup form should validate the user's information and supply errors",
    'The user should receive a confirmation email',
    'The user should show up in our database as confirmed after clicking the confirmation link',
  ],
  mockupLinks: {
    'Invision: Login Form': 'https://invisionapp.com',
    'Invision: Login Form Error State ': 'https://invisionapp.com',
  },
}

ActiveMDX provides me with a collection class, for working with groups of these documents as if they were a database. I can write scripts, or web applications, automated github action workflows, and a bunch of other things, to do things with this data.

ActiveMDX In Action

An example website that is built with ActiveMDX can be found here

This is a website for a hypothetical software project. The goal of this website is to act as a home for the project, so that:

  • stakeholders can get a real time overview of where the project is at. How much it will cost, how much time it will take.
  • the builders of the software have up to date, accurate information about the requirements for the software.
  • the builders can access writing about the domain knowledge they need to understand what they are building, why, and how.

So much effort, people's entire jobs, revolve around processes for achieving these goals. The entire agile software development metholodogy, scrum processes, one big section of the entire org chart for a software company is focused on achieving these goals.

And the tools they use to do this, in my opinion, are huge wastes of creativity and energy.

If these professionals all contributed to the same version controlled ActiveMDX project, then so much of the busy work that drains their energy could simply disappear.

ActiveMDX can help make software ideas a reality from the idea on a napkin stage, all the way to a fully functional, delivered product.

Your idea could start out in a file called Project.mdx.

# E-Commerce Website Idea

Some notes.

## Epics

### Authentication

blah blah

### Content Management

blah blah

### Shopping Experience

### Inventory Management

### Order Management

You can then expand this into several epic documents.

$ amdx expand Project.mdx --to epics

This will create several epic files, which you can edit.

# Authentication

Users need to register and login and stuff.

## Stories

### Registration

As a user I want to register so that I can use the application.

### Login

As a user I want to login so that I can use the application.

You can expand those epics into individual stories

$ amdx expand epics/authentication.mdx

This will in turn create separate story documents.

At that point you can publish all of those stories to a Google spreadsheet and have people prioritize them and provide estimates on how long they will take.

You can then pull that information back into your documents, and version control it.

$ amdx action sheets:sync --name estimates
$ git commit -m "First round of estimates and prioritization finished and added to our docs" -a

You show your client your proposal:

Scope Of Work Example

You send the spreadsheet to the attorneys so they can include it in the statement of work and contracts.

Designers, Product Owners, Architects, aka the suits, should all write markdown and use github and add detailed requirements, acceptance criteria, diagrams, mockups, and all of the things required to build the right thing.

They version control their work like developers do. Github Actions workflows process this writing and does things with it.

For example, you publish all of those stories to Github Issues.

All of this documentation lives right along side the code, and is accessible directly within the developer's environment.

This helps them build the thing faster than ever. They spend less time in meetings, because anyone who wants a view into the status of things only has to go to the website and it is up to date!

The quality of their work is way higher than normal, because all of the acceptance criteria in the stories were extracted out by a tool and used to generate end to end tests. These end to end tests record videos and screen grabs.

When a new version is released, the customer can go to a website and see the changelog that is automatically generated, watch videos of the software working.

Do you understand how much manual effort and technology setup would be required to do this for every project? Without something like ActiveMDX, this would be more work in addition to all of the time spent in Jira and meetings.

The possibilities really are endless.

ActiveMDX can take asynchronous communication to an entire new level within most software organizations, and make everyone happier and better at their jobs in the process.