Author Avatar Image
Alexander Reelsen

Backend developer, productivity fan, likes the JVM, full text search, distributed databases & systems

The ultimate workshop-as-code approach
Jun 20, 2023
14 minutes read

TLDR; This blog post describes my process of creating, preparing and delivering a workshop, including content creation and how to tackle all of this from an engineering perspective.


I was asked at my previous employer to create a workshop allowing prospects could take a look at the platform and gather some first experience. The workshop is optimized for engineers to be maintained and held.

Can’t we just use Google Slides or keynote?

This will likely be the first question, once you outline a different approach to running the workshop.

While many tools allow you to create fancy animated one-time presentations that look like a keynote from Steve Jobs, it is more complex to create maintainable content using those tools. While being great for one-off presentations, features lack that my engineering preferences require for such a project. I saw major training material at previous companies maintained always by a single person, because otherwise maintenance became to complex. Also figuring out differences or going back and forth in the history becomes hard - imagine giving a workshop based on a previous version of your product, because that’s what the participants are using at the moment.

Let’s take a look at my requirements and check them out one-by-one.


Everything must be versioned (aka git repository). I really do not want as a final asset. I also do not want twenty different versions floating around in Google Drive and never know which is the one with the latest changes depending on the person presenting. I want to be able to go back and forth with changes history and support a developer friendly workflow. And only have one version deployed to my live system. That does not mean, that previous attendees have access to that version.

Collaboration ready, bus factor reduction: One of my basic rules of how I do work is to make myself immediately replaceable. This means, whatever I do is ready to be used by others. If this premise does not hold, it’s an error on my side. Raising an issue if content is not correct or even a Pull Request must be straight-forward. The git repository will always be ready for release.

Text-based input format: Markdown or AsciiDoc. Something diff compatible. Whatever is preferred in your company.

Use HTML and PDF as output formats: HTML is the easiest format to consume as it needs a browser and that’s it. PDF may help in distribution, offline consumption, printing (really?) or in an Ebook reader. One could also watermark that content, but keep in mind, most of us don’t do rocket science.

Code snippets with highlighting: This is one of my pet peeves. I deliver technical content, it’s the center of what I deliver. No matter if it is a Java snippet or a SQL statement, syntax highlighting will always help people to identify parts of code. Snippets needs to be looking as good and concise as possible. This is hard with other presentation tools, unless you are going with images.

Automated testing of code snippets: Another one high on my list and hard to do for existing presentation tools. Extraction of code snippets and then making sure they compile or run SQL statements against a database.

Diagrams as code: This is a complex one. There are many tools out there for diagramming like Excalidraw or tldraw, that are really awesome. You end up with SVGs that are hard to maintain via a version control system. However in the recent months there has been some developments of tooling around, that we will cover later.

Labs must be included: Labs are essential for a good workshop to have the participants experience the product you are presenting. Otherwise the workshop is a boring monologue towards the audience. Side effect: If you are the only speaker, you get some speaking breaks.

Content must be accessible via the web: Don’t share content via a USB stick during your workshop, but make sure it can be consumed without hassle, even when the workshop is finished.

No complex learning platform: There are many of those platforms out there, allowing you to manage courses, registrations, assets, etc. That all makes sense at a certain size, or if your workshop is on-demand.

Update process must be automated: Once you a have process to push your assets somewhere, this has to be simplified, maybe even automated. If you need to invoke more than a few CLI commands, than it won’t be done, especially if permissions are needed (OHAI AWS).

With these requirements in mind, let’s check out the technology chosen for presentations.


I’ve switched all my presentations to markdown over the years. I think the de-facto standard for this used to be Deckset on osx, but then the markdown driven browser based presentation tools arrived. I remember doing my first public Elasticsearch presentation in 2011 using Shower and then moving over to reveal based presentations in 2012. Reveal.js is still around today and going strong, even featuring it’s own hosted presentation platform.

I stuck with Marpit. I used Marpit for most of my presentations in the last years and thus had the best experience. It is not without issues however. The biggest risk is Marpit being mostly a single developer project. However responses on GitHub are fast and I use the most basic features only.

Alternatives nowadays are probably Reveal and more recently slidev with the ability to also have more interactive blocks, if you are a fan of Vue.

Presentation Structure

Let’s take a look at a sample Marpit file. One needs two files: A markdown file with the content and a theme file. That theme is referenced in the front matter of the markdown file:

marp: true
theme: cli-2023

A slide looks like regular markdown, slides are separated using three dashes:

# Search Search Evolution

### The next generation of search engines...

Alexander Reelsen | [@spinscale](

However you can go more fancy by embedding CSS styling in the markdown file as well within any slide by specifying a scoped style only relevant for this slide:

<style scoped>
h1 {
  margin: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 5em;
  white-space: nowrap;

# Centered title


Once such a scoped style is used across several slides, creating a class in the theme file and reuse it like this is the best option:

/* @theme cli-2023 */

@import 'default';
@import url('');

section.centered h1 {
  margin: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 5em;
  white-space: nowrap;

Then you can specify the centered class in your markdown slide and end up with title in the middle of your presentation screen.

<!-- _class: centered -->

# Centered title


There are a few more tricks, i.e. the ability to specify a background image for every slide. I tend to try and copy the brand/CI a little bit making slides immediately recognizable, without investing too much time in.

I use a Makefile involved to remember all the commands I need to run - again that is nothing I would like to put in the docs, if I can put all the command line parameters into code

    marp -s . -I . -w --allow-local-files --bespoke.progress true

Marpit is a CLI tool, but you can customize the rendering engine and add features like per-line/multi row highlighting within code snippets. I have always been too lazy for that, but you do need this kind of features for a good workshop. Other presentations tools have something like that built-in.

Asset export

As my repository contains several markdown presentations, I add another snippet to my Makefile to create HTML/PDF versions of my slides. Did I mention, that some technologies never fade? At least there are no m4 macros.

    ./node_modules/.bin/marp --theme-set . --pdf $(FILE) -o /tmp/$( --allow-local-files

    ./node_modules/.bin/marp --theme-set . --html $(FILE) -o /tmp/$( --allow-local-files

Running make pdf creates a PDF in the /tmp/ directory.

Diagrams as code

Not everything fits into the x-as-code mantra, I am sure at some point you will need to use some tool like Excalidraw or tldraw. Both can also export to VCS compatible formats, but that’s a little more work as it requires manual steps.

The two tools I tested can be used as part of the build process. I can store the output SVG files in the git repository as well, there is no need to create assets on the fly.

First up is d2 - declarative diagramming. You can draw standard forms and rectangles, have icon, image, text & code support, plus some more advanced diagramming like LaTeX, UML, sequence diagrams and even SQL tables. The latter one was the main reason for me to use it, plus the ability to theme it. Also, there is nowadays a capability to have staged builds of SVGs named scenarios. One can create a single file and can create SVGs for multiple slides out of that. While d2 is really powerful for the supported kind of diagrams, it’s not a one-size fits all solution. Also, check the extensions page, as support for build systems or reading the schema from different databases like Postgres, MySQL or MongoDB have been added recently.

Second, if you need ASCII art in your images, you should take a look at svgbob - the link goes to the playground to try it out. I used for a few minor things in my regular presentations, but have not yet used it in a workshop.

Alternatives: diagrams for cloud architecture diagrams written in python, mermaid (seems to feature a quadrant chart recently, for sure enterprise ready), KLayJS/elkjs.

Missing: Tools for nice charts.


Labs are always highly dependent on the platform you want to show. Is it on-premise, SaaS or both? Regardless, you should never make folks install any software locally on their own notebooks, as you can never be sure how hard corporate notebooks are locked down. Even docker may be an issue. Best if everything works in browser (one of my previous employers was a heavy strigo user, that even a few years ago was already nice, as no preparation was needed on the attendees side.

For the sake of simplicity using the SaaS software and have a single page for the Labs sounded like a good plan in my case.

As Labs should be accessible after the workshop event, they also need to be combined with solutions. However those solutions should not be immediately visible. For this workshop design I went with a HTML document, that contains markdown (which is rendered on-load) and a <details> tag, where only the summary is visible, and it is clicked on. Like this:

Optionally the solution can be copied into the buffer with one click, ready to be pasted into the workspace.

In my case, labs are a single HTML file, using marked and highlight.js in the browser, plus a little bit of custom CSS styling. Having markdown in HTML allows easier editing and better diffs, the load and rendering time is negligible within the browser.

Automated testing of code snippets

One of my favourite features of this whole setup and why I think going this route makes sense. The amount of times I gave trainings and workshops and suddenly a participant finds a missing comma in a JSON snippet or a some labs are not working because the workshop has been upgraded to the most recent version of the product - is countless. I really hated that moment, because it showed that we have not properly tested the workshop. There is only one solution to this: automation.

You need to be able to extract all the snippets from all assets for a workshop and run them against a running instance of your product. As everything in my workshop is markdown, extracting code snippets is straight forward. Thanks for syntax highlighting just extracting SQL snippets required looking for lines containing


Sometimes you are listing incomplete snippets, that would always fail. These need an extra marker not to be tested like this:

```sql test-ignore

Adding the test-ignore part allowed the testing script to ignore this snippet for testing.

I ended up with a highly custom script, that started an instance of the product, ran all the extracted snippets against that instance and shut it down again.

As long as this script is running before every workshop I can be sure to catch issues. Keep in mind, that the underlying product can change anytime, putting this into CI or a GitHub action makes sense. If your product is doing continuous deployment, you may even get those issues while running the workshop, but I consider this a major risk reduction.

Provisioning of the demo environment

If you are lucky you can skip this step, because your service is quick to setup, that every user can do it as part of the workshop. I think this needs to be the goal and everything else is a workaround. This also removes additional complexity like user & password management. But let’s imagine for a second this is not the case:

As already mentioned above, running on attendee hardware is the worst option and you need to find a way not to do that. If you are having a SaaS service, this requires no provisioning in the best case and some provisioning in the worst. This provisioning code needs support the lifecycle of a workshop:

  • Create service instances
  • Start service instances
  • Optionally: Configure service instances (i.e. locking them down)
  • Optionally: Link with 3rd party services
  • Optionally: Load sample data
  • Post workshop: Stop service instance
  • Post workshop: Delete service instance

All of these are bulk operations against all of the instances of a workshop. You may need to be able to run any of those operations against a particular instance as well - for example when a restart might help solving a problem.

Also you need to cater for the fact that you might need to run those workshops in parallel, make sure your provisioning supports that.

Again, if your SaaS service has a free tier and allows free registration, then just running through the registration and starting the service should be the first step in your labs.

Asset sharing & deployment

The labs and presentations need to be shared during the workshop for fast access. These assets are often not supposed to be publicly in the internet, as workshops and trainings can be used for lead generation or product awareness (outside of open source), may contain credentials or test data, or may even be upgraded to a paid training in the future. Remember: This stuff is not rocket science and if your product is not open source, then having your material open may help you.

One solution could be to use Docker to create all your training assets and then use any platform that supports docker containers to host that and pay a few bucks a month for that, if you need some protection like basic auth.

If you don’t care for hiding your assets, web hosting using S3 bucket/GitHub/Netlify might be a good idea.

It’s still chaos, but better

While I solved a fair share of issues (from my developer perspective) how to run workshops, the experience for participants is still mediocre, as the amount of different systems involved is still too high:

  • Registration on the website
  • Notification of attendees via email
  • Log into your own service (SaaS)
  • Asset sharing with attendees, possibly with a second login
  • Live webinar via a conference software
  • Post workshop email to visit the company website again

Also from a company perspective you may want to come up with proper analytics about participants, i.e. knowing who finished the labs. Luckily I could figure this out and get a first impression of who really is interested in the product or was more after an overview.


I’ve probably mislead you with the title as this is still not the best experience for attendees, it’s less ultimate than it sounds from a organizer lens.

One more aspect in all of this is the fact, that people running workshops are often not interested in having a git based workflow and are indeed happy with google slides or keynote. This may lead to a lot of friction within the team delivering the workshops.

I think the points in the last section can all be solved with some coding investment and when delivering workshops is part of your company strategy for product adoption, they should.

I still have some bad opinions against MDX, but that might become the de-facto standard for this kind of apps, as it allows to have more interactive components in your presentation, probably even allowing to communicate with your product directly.

Final remarks

If you made it down here, wooow! Thanks for sticking with me. You can follow or contact me on twitter, GitHub or reach me via Email (just to tell me, you read this whole thing :-).

If there is anything to correct, drop me a note, and I am happy to fix and append a note to this post!

Same applies for questions. If you have question, go ahead and ask!

If you want me to speak about this, drop me an email!

Back to posts