Using generators to improve developer productivity

Using generators to improve developer productivity

7 min read

A few weeks ago at N26, we did get stuff done week, during this time the product and engineering teams have the opportunity to try or build anything we like.

I decided to spend the week adding a code generation tool to the Web project automating a bit of the coding work while improving the developer experience.

In this article, I'll walk through the experience and the outcome of implementing a tool to generate code ๐Ÿ‘จโ€๐Ÿ’ป

Table of Contents

  1. What is a code generator?
  2. What's the problem?
  3. Why you should use them?
    1. Developer experience
    2. Decision fatigue
  4. Using code generators
    1. Choosing the right tool
    2. Getting started
    3. Creating a generator
    4. Demo
  5. Automate all the things!

What is a code generator?

A code generator is a tool that given a set of rules and inputs creates code, files, and folders.

To name a few popular ones ๐Ÿ‘‡

All of them will create code based on specific rules taking into account the inputs provided by the user. Here's a simple example ๐Ÿ‘‡

Code generator teaser

What's the problem?

Imagine, you start the day working on a new task and you need to create a feature, before writing any code you will need to consider a few things:

  • Folder structure and architecture ๐Ÿ“‚
  • Naming files convention ๐Ÿ“‘
  • Where to put the feature ๐Ÿ—‚
  • How you should write the tests ๐Ÿงช

I'm sure the project you're working on has a list of conventions and patterns defined that explains how you should work in the codebase.

However, every time you are going through this process it requires you to think ๐Ÿค” about those conventions to make the decision.

Where is the source of truth? ๐Ÿง

  • Documentation ๐Ÿ“
  • Architecture Decision Records ๐Ÿ—
  • Similar files on the codebase ๐Ÿ•ต๏ธ
  • A teammate ๐Ÿง‘โ€๐Ÿ’ป

As software engineers we work very hard to not repeat code building abstractions, automating manual workflows...

What about writing code? In the same way, we advocate for automating processes such as deployments, we should also make an effort to the non-creative part of coding such as scaffolding.

Why you should use them?

Generating code will save you time โฐ and will increase the productivity of the team ๐Ÿ“ˆ

Developer experience

In a team that is constantly growing it is important to make sure that everyone is aligned and able to build things efficiently.

Having a great Developer Experience will boost the confidence a developer has with the codebase.

Trust, empowers people to be more productive and agile โšก๏ธ, to name a few of the many benefits you'll get by using generators:

  • Ensure everyone is doing things "as expected" according to the project conventions ๐Ÿ’–
  • Reduce friction when working with the codebase ๐Ÿค”
  • Ease the onboarding of new joiners ๐Ÿ†•
  • Decrease the development time ๐Ÿš€

Decision fatigue

The most important thing is that your teammates will not have to spend time on low-value decisions, such as deciding how a component is structured.

Turns out our decision-making process gets worse the more decisions we make.

This is called decision fatigue. For example, Steve Jobs, limited his everyday clothing down to one outfit to avoid making a decision.

Using code generators

Sounds good right ๐Ÿ˜? Let's take a look at how we can implement code generators in our project ๐Ÿ‘€

Choosing the right tool

I didn't want to reinvent the wheel, my focus was set on the outcome of generating code, not building a tool that solves this problem.

There are a lot of Open Source projects that will do an awesome job generating code. Here's the list of the ones I considered:

I decided to go with Hygen at N26 because:

  • Great monorepo support
  • Easy to maintain. You'll only need to care about .EJS template files and prompts.
  • No configuration required
  • High value with low effort, writing a generator is very simple.

Plop is also a great tool but creating a generator is more complex since you need to spend more time writing code and the tool demands extra time on the configuration part compared to Hygen.

Yeoman is another valid option, but maintaining generators requires you to manage and publish packages to a registry and I would say the use-case of this tool is more suited to scaffolding projects instead of smaller parts of a codebase.

Getting started

It's time to create our code generator ๐Ÿฅณ. First, install Hygen as a devDependency in your project:

$ npm install -D hygen

Define a script inside the package.json to use the script binary:

{
  "scripts": {
    "generators": "hygen"
  }
}

Now we can run Hygen through the generators script:

$ npm run generators

After running the command, you'll see that Hygen is telling us we don't have any generators.

Error: please specify a generator.
Hygen v6.2.0

By default, hygen will read the generator files from a folder named _templates.

If you want to change this behavior, create a file named .hygen.js at the root level of your project, like this:

const path = require('path')
 
module.exports = {
  templates: path.join(__dirname, 'generators'),
}

Creating a generator

A generator is composed of one or more actions, every action contains two items:

  • Templates: *.ejs ๐Ÿงฉ
  • Prompts: prompt.js โ‰๏ธ

Let's build a simple generator that creates a React component ๐Ÿค“. At the end of the post, you'll find a GitHub repository with a lot of different examples ๐Ÿ‘

The first thing we need to create is the generator folder that will contain the action:

  • Generator: component
  • Action: react
$ mkdir -p generators/component/react
Templates

Template files define the code that will be created after running the generator, these files are written using a template language called Embedded JavaScript templates.

Every template starts with a frontmatter header. On this header, you will define the metadata of the template using the following properties ๐Ÿ”

PropertyTypeUsage
toStringThe destination of the file once compiled.
fromStringUse an external file as template.
forceBooleanOverwrite existing files
unless_existsBooleanExecute the generator unless file already exists.
injectBooleanInject the contents into an existing file instead of creating a new one.
afterRegexInject the template after the regex
beforeRegexInject the template before the regex
prependBooleanInject the template at the start of the file
appendBooleanInject the template at the end of the file
at_lineRegexInject the template at the specified line number
skip_ifRegexSkip injection if regex matches.
eof_lastBooleanTrim the newline from the end of the injection.
shStringTrigger a shell command after compiling the template

Now, let's add a template file named index.ejs inside the react action folder we created previously:

$ touch generators/component/react/index.ejs

As I mentioned before, we want to create a React component, so we need to specify the location of the file where the component is going to be created.

We can do that using the to property.

---
to: src/components/<%= h.changeCase.pascalCase(name) %>/index.js
---

As you can see, we're using a variable called name on the header. This value will be provided by the prompts of the generator โ‰๏ธ

Then, we need to write the body of the template, where we define the code that will be generated once the template is compiled. I'm reusing the name variable in the body as well to create the name and export of the component โœจ

---
to: src/components/<%= h.changeCase.pascalCase(name) %>/index.js
---
 
const <%= h.changeCase.pascalCase(name) %> = () => (
  <section>
    <h1>Hey! ๐Ÿ‘‹</h1>
    <h2><code><%= h.changeCase.pascalCase(name) %></code></h2>
  </section>
)
 
export default <%= h.changeCase.pascalCase(name) %>
Prompts

In case you need to ask for user input, optionally you can use a prompt file. This is very useful to customize the output of the generator. Prompts are defined using a library named Enquirer.

Inside of the same react ๐Ÿ“, we will create a prompt.js file to ask for the variable defined as name in the template:

module.exports = [
  {
    type: 'input',
    name: 'name',
    message: 'Write the name of your component',
  },
]

There are a ton of different input types available ๐Ÿ”ก, take a look at this link to get the complete list.

Now it's time to finally run our generator ๐Ÿฅณ using the script along with the name and the action:

$ npm run generators component react

You'll be asked for the prompts and finally the magic will happen! ๐Ÿฆ„

Code generator demo

Demo

Take a look at carloscuesta/codegenerators-demo ๐Ÿ•น if you want to play and see more complex examples!

Automate all the things!

It's time for you to find out repetitive tasks and patterns to extract them into a generator! ๐Ÿš€

I'm very happy with the productivity boost and the consistency that a tool like this can bring to a team โค๏ธ


Enjoyed the article? ๐Ÿ˜

Subscribe ๐Ÿ‘จโ€๐Ÿ’ป