code-workshop-kit: a tool for remote code workshops
Tags: javascript, showdev, node, vscode
Published: 2020-12-14 12:41:30

I created a tool for **people that teach code**, called [code-workshop-kit](https://code-workshop-kit.com/). It makes remote workshops interactive and allows for experiences similar to those in a classroom, and better.
In this blog I will explain the problems of **remote** code workshops, why I created [code-workshop-kit](https://code-workshop-kit.com/) and how you can use it.

In today’s world, more and more workshops and trainings are held online rather than offline. Instead of standing in front of a classroom in a real physical location, we often find ourselves educating in front of a digital Teams or Zoom room. This transition has been happening for many years, gradually. 

Then, COVID-19 hit us, meaning that more and more people are now permanently working from home and classrooms with a bunch of people in a small room are a no-go in most places. Even though I am optimistic about the future of this pandemic, it still seems likely that it has sped up this transition of workshops and education. I foresee that even most conferences will embrace digital means of attending from now on, which would include their workshops being held remotely as well.

## Challenges in online workshops

Next to my daily work as a web developer, I am also an educator, giving workshops and full-day trainings usually twice a month or so. My style is very *interactive*, *hands-on* and *exercise-driven*. This requires input from my participants. If you happen to be like me in this regard, you will recognize the following problems when giving such trainings online.

- **No response.**
  When you ask a question to the room of online participants, you will likely find that it’s much less common for people to answer. 
  Part of this is a lack of accountability and responsibility, since a participant is essentially invisible in a group of many other participants that might answer. 
  Even when asking a participant by their name specifically, will sometimes leave an awkward silence, as you did not realize the person was at this moment grabbing a snack, a coffee, having a toilet break or busy shooing away their kids from their improvised home office.

- **Can you share your screen?** 
  Whenever one of your participants runs into an issue, especially if they’re not an expert at explaining code bugs/errors, you end up asking them to share their screen so you can look what’s going on. 
  Apart from the time spent to share screens and get that working properly, you also distract everyone else in the meeting with the issue of that one participant. 
  In a physical classroom, you can walk up, look over their shoulder, and whisper to help them out. 
  This is much more welcoming than asking the participant to essentially broadcast their mistakes to the entire room. I have found that participants are far less likely to ask for help for this reason alone.

These two problems alone in my first workshops online before COVID-19 made things quite exhausting; I really disliked the experience.

It was awkward, inefficient, and many of my participants lost their attention span and I don’t blame them. I figured I would ask my participants to be present, physically, for my workshops and trainings, if possible.

However, after COVID-19 hit this was no longer possible. When I realized all of my future trainings and workshops would be like this, I decided I had to find a better way.
Finding a solution

When identifying the problems, two main requirements arose:

- **Shared code:** I need to be able to see and collaborate on the code of my participants.
- **Shared output**: I need to be able to see the output that the code of the participant produces.

When searching for a solution to the first problem, I came across many tools. Some examples are simple codeshares like pastebin, Github gists, Codepen, JSFiddle, but these are not meant for live collaboration. 
[CodePen Professor Mode](https://blog.codepen.io/documentation/professor-mode/) and [codeshare](https://codeshare.io/) are probably the closest I found to the solution I needed. However, both are closed-source and have their limitations which I figured would end up biting me. 

The final contender is perhaps one you already thought of: [Visual Studio Live Share](https://visualstudio.microsoft.com/services/live-share/), in my case the [extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare-pack), or as I like to call it, the “Google Docs of coding”.

This puts collaborative code sharing and writing, in what also happens to be my favorite code editor of all time. It is open-source, has an Extensibility API to write or extend upon VS Code extensions and a bunch of other features, most notably **Shared Servers**.

So with this, my first requirement was met, kudos to the Live Share team at Microsoft for creating such an incredible product! Shared servers inspired me for the second requirement.
With shared servers, any participant that is connected to your session has an SSH tunnel to you over the port you share in shared servers. This means that they can use `localhost:<port>` on their machines and access a shared API or web server.

However, there are remaining challenges:

- See the output of backend languages where the output is not a served web application or module
- My participants will all have their own code, and therefore their own output. How do I easily distinguish and view a specific participant’s output?
- Seeing an overview of all the outputs of the participants, so I can easily glance at all of them simultaneously to see if I find a participant who is lagging behind or stuck on something
- See updates of the output whenever a participant saves a file inside their folder without refreshing the page

## code-workshop-kit

This is essentially where [code-workshop-kit](https://code-workshop-kit.com/), my NPM package, comes in.

It is a smart development server that is built on top of a revolutionary, buildless, development server called [@web/dev-server](https://modern-web.dev/docs/dev-server/overview/), which is the successor of [es-dev-server](https://github.com/open-wc/es-dev-server), built by the guys from [Open Web Components](https://open-wc.org/).

This server is essentially an abstraction on top of [Koa](https://koajs.com/), making it easy to write plugins and middleware for serving files over HTTP, and it has a really good NodeJS API for extending and building on top of it to make your own *opinionated* dev server. This is exactly what I needed to overcome the remaining challenges.

What the code-workshop-kit server does on top of `@web/dev-server`, is ensure that when serving the main `index.html`, an app shell component is inserted which, among other things, has:

- Basic user authentication for participants and hosts
- An overview page on which the output of every participant is aggregated to
- Hot Module Replacement for frontend workshops
- Follow-mode, allowing the host to switch pages on localhost and the participants’ URLs are updated to follow
- An Admin-only UI bar for toggling dev server settings on the fly

> There’s many more cool features, to read more about this, visit the [code-workshop-kit docs](https://github.com/code-workshop-kit/code-workshop-kit)!

## How to use it

So far, the main use cases are either frontend web workshops or workshops for backend languages, where the terminal is the input/output. So let’s go over those.

Having NodeJS and NPM installed is a prerequisite.

### Frontend

Create an empty folder and install code-workshop-kit:

```sh
mkdir cwk-test && cd cwk-test && npm init -y && npm i code-workshop-kit
```

Open the folder in VS Code.

Create a file called `cwk.config.js`:

```js
export default {
  appTitle: 'Welcome to Joren\'s Frontend Workshop',
  participants: ["Joren", "Bob", "Alice"],
};
```

This creates the default export that the CWK server uses to read the user provided configuration settings.

Now let’s create some starter files for our participants. Create a folder `template`.

In this folder, create `index.html`:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello <%= participantName %></h1>
    <script src="./index.js"></script>
  </body>
</html>
```

Let’s also create a starter JavaScript file, `index.js`:

```js
console.log("Hello, <%= participantName %>");
```

> Note the special `<%= %>` tags in which we can place variables that will be filled in by the scaffolder. Read more about that in the [scaffolding docs](https://github.com/code-workshop-kit/code-workshop-kit#scaffolding)

Scaffold these starter files for all participants:

```sh
npx cwk scaffold
```

You should now see a folder “participants” with a folder for each participant and their starter files.

Let’s see it in action:

```sh
npx cwk run
```

Now check out the browser on `localhost:8000`. Feel free to invite some others by creating a session in VS Code using the Live Share extension.

If you select a name, you should then see the participant overview, and every participant’s `index.html` rendered through an iframe. You can click the view buttons to view only one specific participant's output.

![Participant Overview of code-workshop-kit inside the browser](https://i.imgur.com/piyLcJB.png)

Simple enough right? But it gets cooler.

Right now we render the participant’s webviews through iframes. This is not ideal, especially with a larger number of participants, as iframes are just not that performant. They also live in their own realms, meaning they cannot share dependencies, making this even heavier on the server and slowing down your page.

Very often, in the land of frontend, the main entrypoint is a Javascript file: `index.js`. We then export some sort of template which gets rendered to DOM. This is quite common for Single Page Applications. The benefit of this approach in `code-workshop-kit` is that we can use a technique called **Hot Module Replacement** to reload this exported Javascript module whenever files have changed, without needing a page reset. Furthermore, the overview page can load the module and no iframes are needed, meaning dependencies can be shared easily.

Let’s change our setup to use this method.

In the `cwk.config.js`, edit it to:

```js
export default {
  appTitle: 'Welcome to Joren\'s Frontend Workshop',
  participants: ["Joren", "Bob", "Alice"],
  targetOptions: {
    mode: 'module'
  }  
};
```

This will assume an `index.js` file in the root folder of every participant, which must contain a default export which is either an HTML string value, a DOM Node/Element (`document.createElement('div')` for example) or a lit-html `TemplateResult`. I’m happy to accept feature requests or contributions for other templating methods, as long as they are not locked behind a compilation step. You can always pre-render with whatever tool or engine you like, and pass the DOM node.
Delete your `index.html` inside the template folder.

Edit the `index.js`:

```js
console.log("Hello, <%= participantName %>");

export default `<h1>Hello <%= participantName %></h1>`;
```

Delete your participants folder entirely, and just re-run:

```sh
npx cwk scaffold
```

Then restart the cwk server:

```sh
npx cwk run
```

You should see the same overview. But this time, things are rendered through modules instead inside iframes. Hot Module Replacement also works now. You can see this by going into for example Bob’s `index.js`, and change:

```js
export default `<h1>Hello Bob</h1>`;
```

to:

```js
export default `<h1>Hello Bob!!!!!</h1>`;
```

Hit save, and immediately the application will reload the module, and it gets updated in your browser and those of all other participants, without anyone needing to do a thing for it! As a workshop host, you just sit back and watch the outputs change over time as your participants are writing away at their exercises.

### Backend

At this point, many of you will ask: “okay that’s great but what about my backend workshops, where the output is not a served web application or module?”.

Instead of using Live Share’s shared terminal feature and giving all your participants a separate terminal with full access on your machine, I created a more safe and user-friendly way of aggregating terminal output, which is the most common method of “output” for backend. 

The workshop host has control over which command gets run in each participant’s root folder, and just re-run that command when files are changed. 

Let’s see it in action. Since you have NodeJS installed, I will use a NodeJS example, but I have personally tested this with many other backend languages as well.

> If you want more information, see [docs on using terminal target](https://github.com/code-workshop-kit/code-workshop-kit/blob/master/docs/terminal.md)

Change your `cwk.config.js`:

```js
export default {
  appTitle: 'Welcome to Joren\'s Backend Workshop',
  participants: ["Joren", "Bob", "Alice"],
  target: 'terminal',
  targetOptions: {
    cmd: 'node index.js'
  }  
};
```

Change the `index.js` inside the `template` folder to create a tiny terminal input/output program:

```js
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.question("Hello <%= participantName %>! Where do you live? ", (country) => {
  console.log(`You are a citizen of ${country}`);
  rl.close();
});

rl.on("close", () => {
  process.exit(0);
});
```

Rerun the scaffolder with -f to force overwriting existing files.

```sh
npx cwk scaffold -f
```

And rerun the cwk server:

```sh
npx cwk run
```

You will see a slightly different overview page now, because there is now a clear and a rerun button, as well as a terminal input field which is disabled by default.

![Participant Bob's view of code-workshop-kit for backend, inside the browser](https://i.imgur.com/UIKLd8X.gif)

Try saving one of the files of the participant, or click one of the rerun buttons. This will make CWK run `node index.js` inside that participant's folder, and the output is aggregated to the participant's view. You will see a green circle pop up notifying you that a script is running for that participant, and you can now use the terminal input field to and press enter to send. This will send your text to the process' input.

## Future

At this time of writing, `code-workshop-kit` is v1 (v1.0.4 to be precise). That means the API is stable. Me and two others have personally alpha tested with this a fair bit, both for frontend (usually web component related workshops) as well as Java backend workshops. 

The code is open-source, which I think is fair given that I build on top of existing open-source projects. I also want to reach as many teachers as possible and I want to carry my weight during these difficult COVID-19 times.

This project is not finished. I will continue working on it for the foreseeable future, as it directly impacts my work as a trainer. 
Microsoft’s VS Code Live Share extension team were kind enough to reach out to me and we had a very insightful meeting, so I have many ideas on how to further improve (thanks Jonathan Carter and Filisha Shah).

- Leverage codespaces to allow participants to connect to a session from their browser, without needing to install VS Code & extensions
- Collaborate with the live share team to further improve and suggest new features for their extension
- Leverage the extensibility API of VS Code to further reduce the work needed to be done by the workshop host
- Create more content on delivering high quality workshops, and supplying out of the box working setups for specific languages/frameworks

If you use code-workshop-kit, please do reach out to me, I'd be really happy to know about your experiences and feedback!

You can reach me on [Twitter](https://twitter.com/jorenbroekema) and [LinkedIn](https://www.linkedin.com/in/jorenbroekema/), or send me an [email](mailto:joren.broekema@gmail.com).