Part 6: Refactor

12 Jun 2019

This is the 6th part of the series about writing an serverless Azure Function in TypeScript. Since we now have and end-to-end test up and running we will refactor the code, cleaning it up a bit.

“Make it work, make it right, make it fast.”
– Kent Beck

We have the first part, a working endpoint. Now we make it right.

The code after is has been refactored.

Turn On Strict Mode

Commit

The first thing we do is turn on TypeScript’s strict mode, enabling a range of compile-time checks. You should always strive to enable this when using TypeScript, and when starting from scratch there really shouldn’t be any excuse for not enabling this.

noImplicitAny is one of those checks, requiring that we specify the types of our parameters. For now we settle for simply adding any everywhere.

Return the HTTP Response

Commit

Return the HTTP response from the function instead of setting context.res to have a functional style instead of a side effect. This change simplifies out test code a bit.

I don’t know why the demo function app defaults to using a side effect, and this changes seems like such an obvious improvement to the code, that I wonder, if there is some drawback to using the functional approach that I haven’t yet realized.

Use Strong Types

Commit

We satisfied the compiler by setting the types to any, but we can do better and install type definitions for Azure Functions, available in the @azure/functions package. The package doesn’t include a type for the HTTP response, so here we still use any, Promise<any>.

Using strong types in out tests requires that we mock the arguments we pass in. The best package I could find was @fluffy-spoon/substitute. That package doesn’t support strictNullChecks, so now we unfortunately have to cast a few objects to any in our test. As the author of the package suggests, we could turn off strictNullChecks for our tests, but that approach probably requires that the test files are located in a separate folder.

Rename to greet

Commit: Rename to greet
Commit: Rename to greet.ts

Avoid having a lot of methods named index and avoid having a lots of index.ts files.

Deterministic Behavior

Commit

Delete the destination folder before building to make sure that delete and renamed source files are also deleted in the dist folder.

The packages are installed with the --frozen-lockfile modifier, so that installation fails, if yarn.lock has to be updated. This is how we want our continuous integration to behave, since we want to make sure that the packages installed are the ones specified by yarn.lock.

rimraf is simply a cross platform version of rm -rf, the Unix command for deleting a folder including all subfolders.

Format the Code

Commit

“Nobody likes what Prettier does to their own code, but everybody loves what Pretties does to everybody else’s code.”.

  • Source unknown – drop me a line if you recognize this line

Prettier is a source code formatter that seems to be getting a lot of traction. I don’t agree with all their choices (operators at the beginning of lines, please) but consistently and well formatted is just easier to read.

I use a Prettier extension for VSCode and set "editor.formatOnSave": true, but you can also use a pre-commit hooks to handle the formatting.

Lint the Code

Commit

Use TSLint to verify that the code follow best practices. Since Prettier handles all of the cosmetic rules, these are turned off by extending the "tslint-config-prettier" ruleset.

The world seems to be switching to ESLint even for TypeScript, but I haven’t investigated that yet.

Don’t Abbreviate ‘request’

Commit

I generally try to avoid using abbreviations because I think the code becomes more readable without them.

Remove Unnecessary Log

Commit

Azure logs all incoming requests so there is no need to log a custom message too.

Remove Unnecessary Comment

Commit

Remove the commit since it isn’t really necessary. This information should arguably be part of the documentation included with the type definitions.

Extract Method

Commit

Simplify the code nesting by extracting to a function. We can now avoid the else.

Invert if

Commit

Revert the if to reduce nesting. This also have the nice effect of putting the special case - no name defined - into the if clause, and letting the happy flow flow be the least indented one.

Use String Interpolation

Commit

Replace + with string interpolations making the code more readable. This also have the effect of making it more obvious that we need to terminate our sentence with a full stop.