loading...

Gradually Typing Serverless Applications

ankcorn profile image Thomas Ankcorn ・3 min read

I love creating applications with Javascript. The code is fresh. The feedback loop fast. We remember the properties of that object because we created it just 5 minutes ago.

Maintaining them, not so fun, once the dust settles and our brains are foggy. Attempting to modify and change the code can feel like trying to paint a window with a baseball bat.

We can write lots of unit tests. It helps us catch bugs and prevent regression, but it's time-consuming, and we can only really test the cases we think of first.

The other option is typing our code!

"But hey Thomas we have already written 100,000 lines of javascript, we can't rewrite it all?."

Enter gradual typing!

You can't have your cake and eat it, but you can gain the benefits of static and dynamic types in the same application using Typescripts --allowJs flag. Now we can make a strategic choice about when to use Typescript in our codebase.

Eating Cake

Adopting Typescript like most engineering decisions is all about tradeoffs. In this case, we increase the initial cost of development, in the hope of reducing the cost of maintaining the code.

So we need to apply it precisely to where we know it will have the maximum value. Using monitoring tools we know about the bugs in our software, we must do a route cause analysis to see which bit of code caused the problem. If the problem area is

  • Highly dynamic.
  • Hard to reason about
  • Filled with assumptions

Then it's a candidate to convert to Typescript.

The configuration

First, we are going to do a small amount of configuration to our serverless project. I'm going to use serverless webpack to add typescript support. There are other options, serverless-plugin-typescript for example. But I already use webpack so will take that approach and combine it with the migrating from javascript docs from the typescript website. https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html
https://serverless.com/plugins/serverless-plugin-typescript/

My notes are linked below, but the typescript and serverless webpack docs are much more complete resources on this.

Installing the dependencies

yarn add -D webpack typescript serverless-webpack awesome-typescript-loader

Configuring the serverless framework

plugins:
  - serverless-webpack
  - serverless-offline

custom:
  webpack:
    webpackConfig: 'webpack.config.js'   # Name of webpack configuration file
    includeModules:
      packagePath: './package.json'   # Node modules configuration for packaging
    packager: 'yarn'  # Packager that will be used to package your external modules
    excludeFiles: src/**/*.test.js

Enabling typescript in webpack

const path = require('path');
const webpack = require('webpack');
const slsw = require('serverless-webpack');

const rootDir = path.resolve(__dirname);
const serviceName = slsw.lib.serverless.service.service;

const srcDir = path.resolve(rootDir, 'src');
const nodeModulesDir = path.resolve(rootDir, 'node_modules');
const outputPath =  path.resolve(rootDir, '.webpack', serviceName);

process.env = Object.assign(
  process.env,
  slsw.lib.serverless.service.provider.environment,
  { NODE_ENV: slsw.lib.serverless.service.provider.stage }
);

module.exports = {
  target: 'node',
  entry: slsw.lib.entries,
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  devtool: "source-map",
  optimization: {
    // We no not want to minimize our code.
    minimize: false
  },
  externals: [
    'aws-sdk' 
  ],
  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          srcDir
        ],
        exclude: [
          nodeModulesDir
        ]
      },
      { test: /\.js$/, loader: "source-map-loader" },
      { test: /\.tsx?$/, loader: "awesome-typescript-loader" }
    ]
  },
  plugins: [
    new webpack.EnvironmentPlugin(['NODE_ENV'])
  ],
  output: {
    path: outputPath,
    filename: '[name].js',
    libraryTarget: 'commonjs2',
    pathinfo: true
  }
};

Conclusion

Typescript is super powerful at eliminating a particular class of bugs. It helps get us faster feedback on our code which is key to programmer happiness and productivity. Gradually typing helps us avoid some of the costs. This makes it an easier sell to teams and businesses who already have large javascript applications.

Also, big thanks to Marcel Cutts who introduced me to the idea of gradual typing in javascript at last years react advanced meetup https://www.youtube.com/watch?v=ie4Bxn122Ag&t=1318s

Posted on by:

ankcorn profile

Thomas Ankcorn

@ankcorn

Software Engineer at @NearST. Organiser @LNUGorg. Coach @codebar.

Discussion

markdown guide