
You want WHAT?
I'm a Back-end Guy
I’m a back-end guy… Through and through. Apart from a brief stint for a 4 week contract that was pure JavaScript, I haven’t touched front-end since 2001!
Ok, so a lot has changed in front-end in the last 5 or 6 years, but I’ve just never had the opportunity to upskill, even if I’ve previously recognised that it’s an area I need to know more about in order to stay sharp in my industry.
As far as I had gotten to date was to research what I would choose to put into a modern-day front-end tech stack but I hadn’t picked up the tools and tried it ‘in anger’.
My entire staring context for this project was that I knew next to nothing about modern front-end development but that if I were to choose a front-end stack it would probably be an app based on Angular 2+ and Redux for state management. I was also kind of interested in this thing called ‘TypeScript’ to provide a more familiar style of programming and, obviously, type-safety. Dynamic languages certainly have their pro’s but I like a tighter programming style than pure JavaScript allows sometimes…
Hence I decided to use this code test as an opportunity to learn modern-day front-end development and tech stacks from scratch and to treat this as a learning excercise and a ‘real’ project. The Code Red NPS Widget is the result, and this website is my attempt to document not only the product, but also my learnings and experiences that I can leverage into the future.
The Problem
The basic brief of the code test was to create a widget that would be plugabble into an existing website and support different options on different pages.
Immediately I intuited that there were likely to be issues with encapsulation and styling. Further research confirmed my suspicions and I quickly discovered that the later even had a colloquial name, ‘leaky styles’.
I was also concerned about my ability to create UI elements from scratch in a short time period and was interested in finding some high-level JavaScript UI components to leverage.
Lastly I was worried about the need to have the widget persist some form of state across browser refreshes with the requirement to have a timeout function where the widget would not ask the user for another round of feedback for a period of time.
Rationale
Architectural Principles
Don’t get me wrong, I love MVC ASP.NET and it has it’s use cases but that’s just not where I see the industry going. I believe in a loosely coupled architecture with a front-end that’s fully encapsulalted within itself and divorced from any back-end infrastructure at runtime. It would require connecting to some form of back-end infrastructure in order to communicate with the ourside world, preferably using a standard REST interface.
The benefit of such an approach is that the architecture is loosely coupled and testability is greatly improved. There’s also the added benefit that one can expose one’s back-end REST interface via an API gateway and ‘publish’ a public API, getting all the perks of joining the API economy.
Front-end
From the start I was looking for a solution that would provide a high level of encapsulation and abstraction. I had previously heard about the idea of bundling and to my delight discovered ‘Webpack’ and all it has to offer, including the ability to create a library package that was easy consumed in a variety of ways but created modules that would provide a good level of encapsulation.
The second piece of the puzzle was the discovery of PWA tech and the Webcomponents / Polymer combo. With it’s implementation of Shaddow DOM within the concept of DOM modules, I suddenly had a robust, encapsulation mechanism that not only circumnavigated leaky styles, but gave me all the high-level UI components that I would need for this project. There was even a component that provided an easy abstraction for browser local storage. Polymer on Webcomponents provided the solution for all of my requirements / architectural concerns.
On a larger project I would probably look into using Angular 2+ or React. For the purposes of this project I felt that Polymer / Redux was enough and that not only would Angular 2+ be yet another thing to learn (oh to have more time!) but would simply have been one step too far.
I felt TypeScript was a different propositio though. Fully aware that by using TypeScript I was adding complexity and having to learn more new ‘stuff’, it proved to be the right judgement call. It provided me a development experience much more akin to my experince and style, wasn’t particularly hard to pick up, and of course makes the code type safe and easily testable.
As an FYI - for this project I was never particularly concerned with the UX or design of the widget. I was much more interested in proving the functionality of the choices I was making and still write clear, succinct code on a tech stack and environment of which I was inexperienced. I took the wins with the UX that came with my choice of UI components, to be upside.
Back-end
As a first principle, I will always choose PaaS over IaaS for ease of use and maintainance, unless there’s a compelling reason otherwise. Serverless technology seems to be the next logical evolotution of that idea, so I wanted throw Azure Functions into the mix and add it to my learnings. And then also Cosmos DB so I could learn that too, which again proved to be a good call as it seemed quicker to implement than experience tells me would have been the case with SQL Database in Azure.
Unit Testing
The unit testing for this project is nowhere near where I would like it to be. However, everyone who knows anything about unit testing, knows that it’s not a panacea that will fix everything and that the results one gets from unit testing are only as good as the tests themselves.
I also believe that Code Coverage is a vanity metric. It can be useful to a point but one needs to concentrate more on the tests that add value. It’s always a judgment call. As an example, I specifically chose NOT to try unit testing the database repository function in the back-end as, with the Cosmos DB components in the form they are, the test would be virtually meaningless. To get any value out of a unit test for the repo, I would have had to code a wrapper and an interface for the three objecs used, and I decided my time was better spent elsewhere. But as part of a balanced approach to my development practice, it’s been noted in the Tech Debt section so that it can be remediated at a future time.
Overall, as there was already so much to learn for this project and so little time, I decided to prove the concept of unit testing within the front-end and back-end stacks rather than concentrate on any semblance of code coverage.
The major piece that’s missing is the implementation of an IoC container and corresponding DI in the front-end JavaScript. This would allow the NPS Widget itself to be unit tested, rather than just the services it consumes. I left it out due to time constraints but having engaged in some research felt confident that there were a number of JavaScript libraries available to implement this in the future. Of course, all the front-end service code has been written in TypeScript so it’ll be easy to refactor the widget to support an IoC container when the time comes.
Pain Points
There were a lot of learnings from this project and, quite honestly, a fair bit of pain. I’m reconcilled to the pain though, as I’m fully aware that it’s purely a consequence of my own decisions to use the tech stack I chose, and the versions thereof.
I couldn’t document everything, but following is a list of some of the issues that either really hurt and took time to workaround, or are personal bugbears that I found frustrating:
- Getting a script together that would load the widget across all browsers was a trial. I actually proved that importing Webcomponents via a loader or via the full bundle resulted in different behaviour. The solution seems simple in hindsight but held me up significantly at the time.
- Unit testing Azure Serverless Functions is hard! MSBuild creates the project output in the folder specified in the project settings but puts everything, compiled binaries and dependencies, in an extra ‘bin’ folder under the folder specified. Test runners have a problem finding the classes in referenced projects. Hence I needed to incorporate the third party ContextResolver into the same project as the Function App. And even then, only the Visual Studio and Resharper test runners could process the result. My preferance is to use NCrunch but it doesn’t support Azure Serverless at this time.
- There seems to be a problem with the JavaScript mocking framework where TypeMoq appears to not be working as per the spec. I’ve tried rasing an issue on the project’s Github, but activity from the owners of that project seems to be non-existent. To move forward one would likely need to choose a different mocking framework. For this project’s current purposes however, the simple test case that works is enough to prove that the unit test infrastructre is workable. Out of interest, the TypeMoq issue is detailed on Github.
- CORS was a real pain to solve. Early testing with Postman making calls to the back-end didn’t reveal any connectivity issues. In fact, I’d never even come across the OPTIONS http header before and it took a significant amount of time to solve as CORS needed to be dealt with in not just the back-end code, but also in Azure’s hosting environment which adds a futher level of protection.
- One of the last and most frustrating issues is the visual behaviour of the widget in non-Chrome browsers. Throughout the development process I tested across different browsers but only did so hosting the widget in a blank page. When hosting the widget in a real page with CSS defined (i.e. this website), non-Chrome browsers seem to be incorrectly inheriting styles from the page. This was discovered late by me and I’ve been unable to solve it quickly. The Polymer components chosen are specifically meant to NOT behave this way. If I had time, I would style all the UI elements myself, so I’m not overly concerned. It’s just another of those things that chewed up a few hours trying to solve. The issue is detailed on Github.
- There’s project available on Redux offering Polymer Redux bindings. I had figured to use this, but it doesn’t properly support Polymer 3.x yet. There are several branches of the project purporting to have solved the problem but I couldn’t find one that worked. I had to fall back to implementing a listener and state receiver and manually plumbing changes to local properties for the Polymer elements to bind to.
- Redux and TypeSript. A match made in heaven. If it’s figured out. Until then, it’s a match made hell. Enough said.
- It was also quite hard to rehydrate the applicable application state from local storage into the widget at reload and took some time to solve. It was hard due to a number of issues such as the way Polymer was binding to properties, the way Redux behaves when configuring the store with an intial state and most significantly, the order of async events. To solve it I needed to utilse the
PolymerElement’sready()event but I remain unconvinced that this is the best way to handle pushing the saved state back into Redix. I could have chosen to rerun all the Redux actions actions for the saved state but felt it was clunky but probably something I would have needed to do anyway if the ‘Polymer Redux’ binding had worked.
Observations
My main observations are around how Webpack and Polymer add significantly to redressing the stated concerns I had around the project, but the most exciting ‘piece’ for me is undoubtedly Redux.
A criticism that’s been leveled aginst Redux is that it takes a lot of plumbing and wiring to get working.
It does. But it’s worth it.
Application state management is tricky. Really tricky. Redux helps significantly by providing a consistent methodology for controlling state. With Redux, state management becomes consistent and predictable. Normally I find one writes so many ‘exceptions to the rules’ code, one-off pieces that deal with a single isolated problem when dealing with UI state. All these little pieces add up to form a complete mess of unpredictability and unwanted side-effect interactions with other state making the application brittle. Redux seems to solve that.
There’s still some stuff in this project I don’t like, some state management issues, but they’re about the lifecycle of the components themselves not about how state itself is managed. Without Redux the issues would have been much, much worse.
Overall, the question is of course, ‘What would I do if I had to do it all again?’. I think the answer to that, for me, is pretty much what I did the first time round. There’s some newer style PWA libraries on the horizion but I figured it was simply to early too try using them for lack of support in the full ecosystem. But that doesn’t change that Polymer is essentially PWA.
So, would I use the following again in a modern app…?
Azure Serverless - Yes
Cosmos DB - Yes
Nunit / Moq - Yes
TypeScript - Absolutely
Webpack - Yes
Polymer - Yes
Mocha / Chai - Yes
TypeMoq - Probably not
Redux - Shoot me in the head if I don’t
Tech Debt
There’s certainly some technical debt that has been introduced to this project. Acceptable considering it’s a front-end prototype.
The following lists technical debt and any potential actions to remediate it (in no particular order):
- Unit Tesing:
- NpsWidget:
- Need to implement a JavaScript IoC container to provide runtime support for Depnedency Injection and support for unit testing
- Extract logic in
render()method to a seperate class to enable proper unit testing - Extract logic in
constrcutor(config)method to a seperate class to enable proper unit testing - Extract logic in
ready()method to a seperate class to enable proper unit testing - Extract logic from widget event handlers to seperate classes to enable proper unit testing
- Back-end API:
- Interface and wrapper needed for Document DB to allow for any unit testing of value
- NPSSubmission function should be split into two seperate services for unit testing - one for POST and one for OPTIONS - also split out the internal logic of processing the incoming http request so that it can be unit tested
- NpsWidget:
- Webpack / Bundling:
- Need to reduce the size of the bundle
- Check out code splitting features of Webpack
- Implement importing only the required methods from LoDash
- Import only the icons used in the widget rather than the whole icon-set
- Investigate why trying to load the widget via an AMD loader fails
- Need to reduce the size of the bundle
- Architecture / technology / project structure:
- NpsWidget:
- Should make the Redux state management classes immutable - investigate the ‘immutable.js’ library (which would remove the need for LoDash altogether and reduce complexity in the Redux reducers)
- StyleDefinitionsMapper doesn’t add value after refactor - can be replaced with a standard
IDictionary<string, string> - AnswerValuesValidator remains un-implemented
- Submitting the Feedback details is ‘quick anbd dirty’ - need to make a couple of new
<actionThunk>objects to process and close and the submission correctly - Remediate the rendering of visual styles of the Feedback poopup in non-Chrome browsers
- Add the widget release version as an internal property
- Add meta data to API call to track: version, widgetName, url, config, date etc
- Investigate whether we can reduce complexity for widget implementors by putting the logic from the render() method into the ready() method
- Remove the
state receivercomponents and replace it with Polymer Redux when it properly supports Polymer 3.x., removing the local properties bound to Polymer and adding the correct state paths for Polymer to hook into instead - Review the way that state is loaded via local storage when the widget loads
- Back-end API:
- Investigate whether we can extract out the back-end ContextResolver into a NuGet package as it’s third party tool and still support the limitations of using test runners to unit test Azure Serverless Function project types
- NpsWidget:
- Error handling:
- Research and implement a correct error handling strategy for both front and back-end code bases
- Remediate that there’s no front-end error handling