How Templates Can Help Onboard -- and Keep -- Your Users

Good documentation and happy developers go together like peanut butter and jelly

When I was in fourth grade, my teacher had me tell him how to make a peanut butter and jelly sandwich.

I said, “So easy! You just put peanut butter and jelly on the bread!”

He took the jars and put them on top of the loaf of bread. “Noooo!” I said, horrified. “Not like that!”

In order to get to eat the sandwich (with the kind of sugary peanut butter and jelly my mother would never buy), I had to describe in exacting detail how precisely to make the sandwich -- what tools were needed, what orientation each piece of bread should be in -- until at last a “normal” peanut butter and jelly sandwich was created.

Peanut butter and documentation

A lot, if not most, technical documentation stops at the level of “just do this!”  Layers and layers of assumptions are made about what terms mean, what steps have already been taken, how a local environment might be set up, and the technical level of the reader. The result is often confusing or incomplete instructions that result in extra support time and wasted developer time.

The reality is that technical users come from radically different backgrounds and knowledge levels, and in order for documentation to be effective, any user should be able to follow your onboarding process. Even documentation intended strictly for internal company use, which can assume some shared lingo or setup, often fails to meet this bar.

When your documentation says “put peanut butter on the bread,” your users might not know they will need a butter knife, a plate, and the ability to unscrew a lid. These users might try to use their fingers to spread the peanut butter, or use a chainsaw to open the lid. They might get annoyed and stop trying to make a sandwich, or they might post simple, repetitive, or unnecessary questions in your support channel:

“How am I supposed to get the peanut butter out of the jar with the lid on?”

“Do I need one knife to use for both toppings, or one knife for each topping?”

“Do you support jam as well as jelly?”

“How do I get peanut butter off of my chainsaw blade?”

As a software engineer who must both onboard other engineers to my service, as well as be onboarded to countless other services, I have a very simple solution to save all of us time and mental energy. It’s free, language-agnostic, and you probably already have one buried at the foundation of your codebase.

What is this magical solution?

Make your users a template for any and all interfaces with your service. Templates show rather than tell how to use your service, and are more likely to communicate unrecognized assumptions to your users.

What is a template?

The word template can refer to any number of objects, from a sewing pattern to a pre-designed but empty slide deck, and they even exist in nature in DNA. In all cases, a template communicates a preset format to eliminate unnecessary reinvention. In this case, templates are the code blocks in your documentation that users can copy, paste, and customize to use as a clear model for onboarding to your service.

In its simplest form, a software template is an explicit agreement. If I give you a template of how I expect your sandwich request, you should be able to fill it out and return it to me to get the sandwich you want.

Say I give you a sandwich.yml file that looks like this:

    # Ariel’s Sandwich Maker Service: Hot Dogs Accepted
# Supported bread and filling types can be found [here]

name: my_example_pbj_sandwich
bread_type: white
fillings:
-	peanut_butter
-	strawberry_jelly
crust: on # optional, default: `on`, options: `off`
cut: diagonal # optional, default: `parallel`, options: `diagonal`
served_on: white_plate # optional, default: `white_plate`, options: `napkin`
  

This tells my user exactly what I am expecting: YAML-formatted snake case fields in a specific order, including optional parameters, and supported customizations. It tells you bread_type accepts one argument and fillings accepts many. It points you to additional documentation regarding sandwich options. By omitting options for jar unscrewers and knife types, it tells users that the service will handle opening the jars and spreading the fillings. A user could easily copy, paste, and adapt this file to use with my service.

Keeping your templates fresh

One of the downsides of templates, and of all written documentation, is that it requires maintenance. If I decide to stop counting hot dogs as a sandwich, and instead spin that off into a separate service, I need to then go update my documentation to make that clear. But let’s be honest: documentation starts going stale as soon as it is written.

Codebase maintainers have two strategies:

  1. Set time aside to regularly (say, quarterly) confirm that the documentation conforms with the current requirements, using Github issues, feedback from users or folks staffing support lines, or manual tests; and/or
  2. Use an auto-documentation method like Swagger, Read the Docs, or Sphinx. Or build your own!

I recommend using both concurrently.

Swagger can detect the requirements of an API and create an easy-to-use UI that defines how to use each endpoint -- what it is expecting, and what it should return. Swagger both makes it easy for users to onboard their APIs to Swagger, but also makes it easy for their users’ users to understand their API requirements. Swagger is basically a meta-template. This article walks you through how to set up Swagger CodeGen, which, among other things, can generate documentation automatically. As a frequent user of Python Flask, I have taken to using Flask-RESTX, which autogenerates the autogeneration, since the best amount of work to do is no work at all.

A tale of three templates

I was inspired to write this article after working with one really good template and one really bad template -- and one template I had to write and maintain myself.

The Good Template

As a backend engineer responsible for my products from end to end, integrating with enterprise and external services is part of my daily life. One such product, which I will refer to as GrilledCheese, allows users to programmatically manage their cloud assets with simple JSON requests. Thousands of engineers use it daily for essential work. You can imagine, if their documentation was lacking, their support lines would be clogged with urgent messages and frustrated customers. Fortunately, GrilledCheese has excellent documentation, with clear descriptions of requirements and copy-and-pasteable templates. The result is a widely-used product that saves immeasurable developer time. While there are certainly still open questions from developers, basic questions are minimized due to their thorough and accessible docs.

GrilledCheese is a great model for templating and documentation, and how good documentation leads to happier users and wider adoption.

The Bad Template

Another product I have recently used, let’s call it Hoagie, provides my favorite kind of template: a full GitHub repo. Ideally, I could clone this repo, fill in the blanks, and be up and running in a matter of minutes. Not so! While the parent application is being actively developed, the template repo was never updated and has since fallen several versions behind. Most of the libraries were out of date and some no longer worked, the ATDD tests failed, and the code itself was not in line with what was actually required by the service.

As a result, there were dozens of issues open with irritated users pinging again and again requesting responses and updates. Their support channel was flooded with repetitive questions, and replies were slow. Existing users stopped recommending the service and now steer new users away from starting to onboard. Attempts to update the template by users are stymied, as there are so many layers of updates required and few available owners to approve changes.

Reader, don’t be like Hoagie.

The Ongoing Template

When I first started on my current team, most of the APIs in my department were written in Java. I had a greenfield project that was a perfect fit for Python, and I ended up making MeatballSub, the first Python API in recent memory.

While I was building MeatballSub, a colleague recommended creating a template repo of my project so that others could easily create new Python APIs. It was surprisingly quick for me to create a template by copying MeatballSub and removing all the specifics -- the meatballs, if you will. What was left was a generic Python Flask application, Sub, that already had a health check, tests, and a dummy endpoint to demonstrate usage. I packaged in all of the Capital One-specific requirements and best practices, and documented how to get it running. Over the past year, I have updated it a few times, just to make sure it still works, or to clarify or streamline an element that a user had trouble understanding. I have to remember to check it now and then, but the amount of time I spend maintaining it is minimal.

My primary takeaway from making Sub was that in order for people to use it, they have to know it exists and where to find it. I needed to present it at relevant showcases, link to it in relevant channels, and recommend it when a good use case crossed my path. If a template exists but nobody uses it, does anyone care? No. The GitHub Blog had a great article recently on this discoverability problem. Make sure you socialize and advertise your docs and your templates. And if that isn’t what you like to do, see if you can get others to market them for you.

Documentation guidelines to increase impact

A template is among the easiest, fastest, and most effective ways for a user to understand what your service is for and how to start using it. Unlike overly wordy documentation (or worse, no documentation), a template shows the user how the service works and communicates details that may seem so minor as to not be worth documenting. (How many times has an integration not worked on the first attempt because the case was wrong?) A glance can quickly demonstrate the requirements and process, and allow a user to independently determine if your product is a good fit for their use case.

All of software is subject to natural selection; some services figure out how to survive and evolve and spread, and others die off and are replaced. The good news - a species of lizard who evolved to only eat sandwiches could only survive or thrive within a narrow window; you actually have some control over whether your code survives or thrives.

If you want your code to go forth and multiply, follow these guidelines:

  1. Write documentation! It’s a low bar, but we have to start somewhere.
  2. Write documentation that people outside your team understand! The only way to know you’ve done this is to talk to people outside of your bubble. So go ask a stranger to read your docs and tell you where they are bad.
  3. Create templates that users can clone or copy and paste!
  4. Use auto-documentation tools!
  5. Make sure all docs and templates are linked in the places that people will look for them! Talk about them, present them, link them, pin them, market them.
  6. Treat your documentation like code! Include doc updates in your acceptance criteria and PR templates, and get in the habit of updating docs in every relevant commit.
  7. Make sure your documentation is up to date! Every three months, go find a new stranger to read your docs and make sure they still make sense, or listen to your users and edit as necessary. Have new team members use your docs to onboard, and empower them to make updates and clarifications.
  8. Acknowledge that without comprehensible documentation, your code will go extinct because nobody will use it! Stop neglecting it.

I’m always interested in hearing your solutions to keep your documentation fresh and low maintenance -- let me know if you have a strategy that works for you beyond what I’ve discussed here.


Ariel Diamond, Software Engineer

A queer backend software engineer at Capital One who loves talking about Python, financial wellness, women in tech, efficiency, rabbits, and cocktails.

Related Content