How to simplify project setup with Projen
Project setup as code
9 min, 1792 words
Whenever you start a new software project and set up a repository for it, what do you do? Run a package manager tool (npm/yarn init, poetry init, cargo init, etc.) to get some starting items in place, then start to copy various configuration files from previous projects that were useful?
Or use scaffolding to set up an initial project setup, and then start copying files from other repositories that have valuable configurations to tweak these?
Or have a repository template as a starting point and work from there?
Either way, setting up a project can sometimes be time-consuming work. There are testing tools, linters, version control workflows, CI/CD pipeline setup, and all sorts of things that add to the quality of life for a project but require time and work.
I have done and used all of the above, and while I usually can get some decent starting point for a project, I still need to spend some time tweaking things a bit more each time.
These issues are why Projen piqued my interest.
Projen (https://github.com/projen/projen) is a scaffolding/templating tool that uses code to generate different configurations your project might need. That in itself is not new or different, as other tools do that. However, this tool not only generates the initial configuration but also maintains it throughout the project lifecycle. It emphasizes that you should not touch many of the configurations by making the files read-only.
It uses programming constructs to build a simple, typed and opinionated interface for various software project types. It then generates the actual configurations based on what you have set in this simplified interface. You make changes through the Projen setup file, and it will generate the new configuration - nothing to touch in the actual configuration files.
I do a fair amount of work with the AWS Cloud Development Kit (AWS CDK).
Elad Ben-Israel, the creator of Projen, is also one of the developers behind AWS CDK. AWS CDK is a software framework for provisioning AWS cloud infrastructure through code, which also tries to hide the complexity of cloud infrastructure behind programming language abstractions. Elad referred to Projen as "CDK for software projects".
Here is a video (15 minutes) where Elad introduces Projen, from last year (sound is a bit choppy in the beginning):
Setting up a project with Projen
But enough talk, let us get practical!
I have Node.js v14 installed, as well as Yarn 1.22 in my environment. The latter is not strictly required but is the default package manager used by the AWS CDK app project in Projen, so I have that in place. The code I show will be in VS Code, but there are no specific editor or IDE requirements.
I have decided to play around a bit with AWS CDK version 2, which is currently in developer preview. So I will create a project to use for that.
First, let us create a directory for our repository and do a git init (not required, Projen can do that for you if you want). After that, I am running projen using npx (npm exec), asking it to use the awscdk-app-ts project type, with the option --no-synth. This option means that it will not run the code to generate the configuration, only set up the baseline file to use with Projen, .projenrc.js.
To run the code and generate the project setup, we can run
npx projen in the project directory.
It will detect a .projenrc.js file in the directory and execute it, to generate the project setup.
The output is several additional files, as shown in the picture below.
If we had not specified the --no-synth option, the execution would have been done when we created the project.
The files include configurations for Github workflows, dependabot (dependency checker in Github), code coverage, Typescript configurations (including specific ones for ESLint linter tool and Jest testing tool), license file, ignore files, package management files, etc. More options are available that are not included by default as well.
All these files may feel a bit overwhelming - what should I do with all of these? The point here is that you do not have to care (much) - you do not need to understand all the details of all these files. Many of these files are even read-only so that you do not tamper with them by mistake. Any changes come from .projenrc.js (or .projenrc.ts, if you prefer a Typescript version of it), which you execute to generate the configuration.
In the same way, as we can create abstractions and simplifications of complex computation in the software we make, we can also simplify software project setup.
We can easily apply any updates and improvements we want to take advantage of for the project setup.
We just run
npx projen with the updated project-type components.
A brief look into dependencies
One of the files generated is package.json, which contains many different pieces of information used in a Node.js environment, including dependencies. An excerpt from the generated package.json looks like this below.
We have two AWS CDK dependencies included that use version 1.95.2. This version is the default version specified with this project type with Projen. There are also developer dependencies included, including Typescript itself, Jest testing framework, ESlint, and other things.
An always included dependency in a solution based on AWS CDK is @aws-cdk/core. The sample code (if there is no src and test directory) uses this dependency.
However, earlier in this article, I talked about making a project configuration using CDK v2, which is currently in developer preview. So we want to make some changes here for this. I will delete the generated sample files (src and test), and I will change the cdkVersion property in .projenrc.js to use a release candidate for AWS CDK v2. I will also disable the mergify option (this will remove the mergify config file).
Now when I run the command
npx projen package.json file has new CDK dependencies in it, new versions, and new packages.
The project type class AwsCdkTypeScriptApp understands that CDK v2 uses a different package structure and will accommodate this.
The sample code also uses these new dependencies now.
Tasks and workflows
When you work on a project, you perform various tasks - you may compile code, run tests, interact with version control systems, etc.
These tasks are also part of your project setup.
Multiple predefined tasks come with a Projen project setup.
If you run
npx projen start you get an interactive list of tasks to perform.
You can navigate through this list using the arrow keys to select a task.
You can add your task definitions, by calling the addTask() method on the project object.
Execute a task directly by specifying its name as well, for example,
npx projen build.
A task can consist of multiple steps.
I like this approach because you have some tasks already defined from the start. You can add tasks specific to your project as well. This feature can simplify instructions to others on how to get started. It also allows performing various tasks without any detailed knowledge about intricate details.
This article was a basic introduction to Projen, a tool that I think has great potential.
As with any code, you can extend the existing Projen project types to make your own. They do not have to be part of Projen itself. They need to be available as a package NPM can install right now. You can also have a hierarchy of Projen projects within a single repository, so you do not have to have a single repository for each project.
Projen is still a 0.x project, there is room for improvements, but I have found it very useful in the projects where I have started to use it. I can highly recommend checking it out! The project is on Github at https://github.com/projen/projen. I hope this article may have wet your appetite a bit for Projen.
Other future articles will include Projen-based projects as well.