Part 6: Refactor
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
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
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
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
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
“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
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’
I generally try to avoid using abbreviations because I think the code becomes more readable without them.
Remove Unnecessary Log
Azure logs all incoming requests so there is no need to log a custom message too.
Remove Unnecessary Comment
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
Simplify the code nesting by extracting to a function. We can now avoid the else
.
Invert if
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
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.