As a programmer today, it is likely you will find yourself working in a polyglot, or multiple language environment. We have finally gotten to the place where the merits of different programming technologies can coexist peacefully together. We all know developers are an opinionated breed of individuals. I certainly find a comfort level with specific technology stacks over others. That is completely ok by the way, don’t let anyone tell you that your preferred stack is somehow any worse than a different one! You may find strengths/weaknesses across programming languages for different use cases, however having valid evidence to support those claims is way better than simply stating an opinion as fact. Regardless, what is often not okay, is rebuilding functionality in a given language with similar or identical requirements when it doesn’t make a measurable difference to do so. After all, we are not providing business value to our customers (at least directly) which is often our primary objective. Someone could argue certainly that it provides developer benefit, maybe it even makes us more efficient as developers, but often with tight deadlines and difficult objectives to hit, we have to make tradeoffs in the work that the team is able to accomplish for a given project, sprint, etc. This brings us to the topic of today’s post. My development shop has started adopting the AWS CDK as our primary Infrastructure as Code (IaC) solution for greenfield development. I am personally all for this as domain specific languages like HCL for Terraform can feel clunky and cumbersome to utilize with a mindset geared towards for-loops, if conditionals, functions and all the other “goodies” we find in traditional programming languages. In addition, we get strong bundling and dependency management capabilities that help the CDK apps we write feel like any other app we are used to coding!
JSii sounds complicated on the surface, after all generating code into multiple languages is certainly not an easy problem to solve, but with the great tooling built by the AWS team we can largely be “ignorant” to those implementation details provided you adhere to using TypeScript as the base language and follow a few rules for the TypeScript being written. Let’s walk through a simple example construct to spin up everyone’s favorite demo infrastructure an S3 bucket. Note you should have the CDK installed on your shell to begin, instructions can be found here. Start by creating a new CDK construct project inside a directory named “MyAwesomeBucket” using the following command:
This will create a new CDK construct project with a construct name and an associated properties interface matching the directory name. Inside the MyAwesomeBucket/lib directory you will find index.ts. This is your starter class inheriting from the standard CDK Construct class. Here we can define our custom construct and any configuration properties it might need. Replace the MyAwesomeBucket class definition with the following code and remove the MyAwesomeBucketProps interface as it won’t be needed for this exercise:
Here we have a simple construct that creates an S3 bucket with encryption and a bucket policy that will enforce SSL. These are great best practices that maybe we would want to enforce in our infrastructure across the company. With JSii we can take this code and publish it in a language specific registry for our desired targets. The entire organization can now use it, getting all those best practices for “free”. There are only two properties and one resource in this example, but this tiny construct shows the power a larger construct can have in speeding up development and codifying best practices throughout the organization. After all, we can simply new up an instance of this class in our CDK infrastructure stack (after installing the dependency) to create in this case a bucket with encryption and SSL protection already enabled!
So with that established, how do we generate constructs using the JSii tooling for all these language targets? First we should install JSii dependencies into our package.json. Run the following command in your solution:
This command installs the two tools we need for this simple walkthrough. JSii will provide the TypeScript compiler for our construct and jsii-pacmak will provide the tooling to generate language-specific packages from our construct code. Speaking of which, JSii will generate your tsconfig.json file for you, so go ahead and delete that file. The next step requires us to make some updates to our package.json. First replace the script block with the following JSii specific commands:
In addition, we must add an author object property with name and email specified, the repository object property with the url set to the git repository location, a valid license property, a description and a homepage are also recommended suggestions. While these items seem trivial, the JSii tooling is very particular about how the package.json file looks. Finally, we must add a jsii object property similar to the following:
This property tells the JSii tooling where to place the compiled (ready-to-publish) packages and what language targets you want to support. To keep things concise, we will use just Python and .NET. Simply specify your naming configuration for each target and JSii will handle the rest! Now we should be able to run the following command and verify there are no errors (note the watch command is handy to use during development as it watches for changes and runs the build on save, enhancing the feedback loop while working on the code):
Assuming there were no errors we are finally ready to build the packages. As you can probably guess running the package command will do the deed. However, you may find yourself on a development machine without specific versions of tooling for your configured targets. No worries, JSii has you covered with this Docker image jsii/superchain! Choose the image version tag that fits your needs and mount a volume or clone your repository inside the docker image. A command like this one should help get you there if you choose to mount a volume:
Inside the container, change your directory into the MyAwesomeBucket folder, run your dependency installation if necessary and finally run the following:
Assuming everything is successful your JSii outdir (dist if you followed the snippet above) will now have a folder for each target with artifacts ready for publishing to npm, PyPi and Nuget with standard publishing tools!
In closing, JSii brings a ton of value to the table and can satisfy everyone by meeting them at the level of the tools and languages they use daily. Languages are likely to be added over time and with AWS backing/supporting the JSii ecosystem, you can relax in knowing it will be maintained for years to come. I personally am hoping to see an inner-sourcing initiative take off at my company with everyone contributing, consuming and using these packages to ultimately move faster and drive business value.